Antimuster der ereignisorientierten Architektur

Hallo wieder! Im Vorfeld des Kurses „Software Architect“ haben wir eine Übersetzung eines weiteren interessanten Materials vorbereitet.




In den letzten Jahren hat die PopularitĂ€t der Microservice-Architektur zugenommen. Es gibt viele Ressourcen, die Ihnen beibringen, wie man es richtig implementiert, aber ziemlich oft wird darĂŒber als Silberpool gesprochen. Es gibt viele Argumente gegen die Verwendung von Microservices, aber das wichtigste davon ist, dass diese Art von Architektur mit ungewisser KomplexitĂ€t behaftet ist, deren Ebene davon abhĂ€ngt, wie Sie die Beziehung zwischen Ihren Services und Teams verwalten. Sie können eine Menge Literatur finden, die erklĂ€rt, warum (vielleicht) in Ihrem Fall Microservices nicht die beste Wahl sind.

Wir bei letgo sind von einem Monolithen auf Microservices umgestiegen, um das BedĂŒrfnis nach Skalierbarkeit zu befriedigen, und haben uns sofort von dessen positiven Auswirkungen auf die Arbeit von Teams ĂŒberzeugt. Bei richtiger Anwendung haben Microservices uns mehrere Vorteile gebracht, nĂ€mlich:

  • : , . ( ..) . ( ) Users.
  • : , . . , , , , . .

-


Nicht alle Microservice-Architekturen sind ereignisgesteuert. Einige Leute befĂŒrworten die synchrone Kommunikation zwischen Diensten in dieser Architektur ĂŒber HTTP (gRPC, REST usw.). Bei letgo versuchen wir, diesem Muster nicht zu folgen und unsere Dienste asynchron mit DomĂ€nenereignissen zu verknĂŒpfen . Hier sind die GrĂŒnde, warum wir dies tun:

  • : . , DDoS . , DDoS . , . .



  • (bulkheads) – , , .
  • : . , , , , . , , API, , , , . Users , Chat.

Auf dieser Grundlage versuchen wir bei letgo, die asynchrone Kommunikation zwischen Diensten einzuhalten, und synchron funktioniert nur in AusnahmefÀllen wie Feature-MVP. Wir tun dies, weil wir möchten, dass jeder Dienst seine eigenen EntitÀten basierend auf DomÀnenereignissen generiert, die von anderen Diensten in unserem Nachrichtenbus veröffentlicht wurden.

Unserer Meinung nach hĂ€ngt der Erfolg oder Misserfolg bei der Implementierung der Microservice-Architektur davon ab, wie Sie mit der inhĂ€renten KomplexitĂ€t umgehen und wie Ihre Services miteinander interagieren. Wenn Sie Code teilen, ohne die Kommunikationsinfrastruktur auf asynchron zu ĂŒbertragen, wird Ihre Anwendung zu einem verteilten Monolithen.



Ereignisgesteuerte Architektur in letgo

Heute möchte ich ein Beispiel dafĂŒr geben, wie wir DomĂ€nenereignisse und asynchrone Kommunikation in letgo verwenden: Unsere BenutzerentitĂ€t ist in vielen Diensten vorhanden, aber ihre Erstellung und Bearbeitung wird zunĂ€chst vom Benutzerdienst verarbeitet. In der Datenbank des Benutzerdienstes speichern wir viele Daten wie Name, E-Mail-Adresse, Avatar, Land usw. In unserem Chat-Dienst haben wir auch ein Benutzerkonzept, aber wir benötigen nicht die Daten, ĂŒber die die BenutzerentitĂ€t aus dem Benutzerdienst verfĂŒgt. Der Name, der Avatar und die ID des Benutzers (Link zum Profil) werden in der Liste der Dialogfelder angezeigt. Wir sagen, dass es in einem Chat nur eine Projektion der BenutzerentitĂ€t gibt, die Teildaten enthĂ€lt. TatsĂ€chlich sprechen wir im Chat nicht ĂŒber Benutzer, sondern nennen sie "Sprecher". Diese Projektion bezieht sich auf den Chat-Dienst und basiert auf den Ereignissen, die der Chat vom Benutzerdienst empfĂ€ngt.

Wir machen das gleiche mit Listings. Im Produktservice speichern wir n Bilder jeder Auflistung, aber in der Listenansicht der Dialoge zeigen wir ein Hauptbild, sodass fĂŒr unsere Projektion von Produkten zum Chat nur ein Bild anstelle von n erforderlich ist.


Zeigen Sie eine Liste der Dialoge in unserem Chat an. Es zeigt, welcher spezifische Dienst im Backend Informationen bereitstellt.

Wenn Sie sich die Liste der Dialogfelder noch einmal ansehen, werden Sie feststellen, dass fast alle von uns angezeigten Daten nicht vom Chat-Dienst erstellt wurden, sondern dazu gehören, da die Benutzer- und Chat-Projektionen dem Chat gehören. Es gibt einen Kompromiss zwischen ZugĂ€nglichkeit und Konsistenz von Projektionen, auf den wir in diesem Artikel nicht eingehen werden, aber ich möchte nur sagen, dass es eindeutig einfacher ist, viele kleine Datenbanken als eine große zu skalieren.


Vereinfachte Ansicht der Letgo-Architektur

Antipatterns


Einige intuitive Lösungen wurden oft zu Fehlern. Hier ist eine Liste der wichtigsten Antimuster, die wir in unserer domÀnenbezogenen Architektur angetroffen haben.

1. Dicke Ereignisse
Wir versuchen, unsere Domain-Events so klein wie möglich zu halten, ohne ihren Domain-Wert zu verlieren. Wir hĂ€tten vorsichtig sein mĂŒssen, wenn wir Ă€ltere Codebasen mit großen EntitĂ€ten umgestaltet und zur Ereignisarchitektur gewechselt haben. Solche EntitĂ€ten können uns zu fetten Ereignissen fĂŒhren, aber da unsere Domain-Ereignisse in einen öffentlichen Auftrag umgewandelt wurden, mussten wir sie so einfach wie möglich gestalten. In diesem Fall wird das Refactoring am besten von der Seite betrachtet. Zu Beginn gestalten wir unsere Events mit der Event-Storm- Technikund ĂŒberarbeiten Sie dann den Service-Code, um ihn an unsere Ereignisse anzupassen.

Wir sollten auch beim Problem „Produkt und Benutzer“ vorsichtiger sein: Viele Systeme verwenden Produkt- und BenutzerentitĂ€ten, und diese EntitĂ€ten ziehen in der Regel die gesamte Logik hinter sich her, und dies bedeutet, dass alle DomĂ€nenereignisse mit ihnen verknĂŒpft sind.

2. Ereignisse als Absichten Ein
DomĂ€nenereignis ist per Definition ein Ereignis, das bereits aufgetreten ist. Wenn Sie etwas im Nachrichtenbus veröffentlichen, um anzufordern, was in einem anderen Dienst passiert ist, fĂŒhren Sie höchstwahrscheinlich einen asynchronen Befehl aus, anstatt ein DomĂ€nenereignis zu erstellen. In der Regel beziehen wir uns auf vergangene Domain-Ereignisse: ser_registered , product_publishedusw. Je weniger ein Dienst ĂŒber den anderen weiß, desto besser. Die Verwendung von Ereignissen als Befehle verknĂŒpft Dienste und erhöht die Wahrscheinlichkeit, dass sich eine Änderung eines Dienstes auf andere Dienste auswirkt.

3. Fehlende unabhÀngige Serialisierung oder Komprimierung. Die
Systeme zur Serialisierung und Komprimierung von Ereignissen in unserem Themenbereich sollten nicht von der Programmiersprache abhĂ€ngen. Sie mĂŒssen nicht einmal wissen, in welcher Sprache Verbraucherdienste geschrieben sind. Deshalb können wir zum Beispiel Java- oder PHP-Serializer verwenden. Lassen Sie Ihr Team Zeit damit verbringen, einen Serializer zu diskutieren und auszuwĂ€hlen, da das Ändern in Zukunft schwierig und zeitaufwĂ€ndig sein wird. Wir bei letgo verwenden JSON, es gibt jedoch viele andere Serialisierungsformate mit guter Leistung.

4. Fehlende Standardstruktur
Als wir damit begannen, das letgo-Backend auf eine ereignisorientierte Architektur zu portieren, einigten wir uns auf eine gemeinsame Struktur fĂŒr DomĂ€nenereignisse. Es sieht ungefĂ€hr so ​​aus:

{
  “data”: {
    “id”: [uuid], // event id.
    “type”: “user_registered”,
    “attributes”: {
      “id”: [uuid], // aggregate/entity id, in this case user_id
      “user_name”: “John Doe”,
      

    }
  },
  “meta” : {
    “created_at”: timestamp, // when was the event created?
    “host”: “users-service” // where was the event created?
    

  }
}

Durch eine gemeinsame Struktur unserer DomÀnenereignisse können wir Dienste schnell integrieren und einige Bibliotheken mit Abstraktionen implementieren.

5. Fehlende
SchemaĂŒberprĂŒfung WĂ€hrend der Serialisierung hatten wir bei letgo Probleme mit Programmiersprachen ohne starke Typisierung.


{
  “null_value_one”: null, // thank god
  “null_value_two”: “null”,
  “null_value_three”: “”,
}

Eine etablierte Testkultur, die die Serialisierung unserer Ereignisse garantiert, und ein VerstÀndnis der Funktionsweise der Serialisierungsbibliothek tragen dazu bei, damit umzugehen. Wir bei letgo wechseln zu Avro und der Confluent Schema Registry, die uns einen einzigen Punkt zur Bestimmung der Struktur der Ereignisse unserer Domain bietet und Fehler dieser Art sowie veraltete Dokumentation vermeidet.

6. AnÀmische DomÀnenereignisse
Wie ich bereits sagte und wie der Name schon sagt, mĂŒssen DomĂ€nenereignisse auf DomĂ€nenebene einen Wert haben. So wie wir versuchen, die Inkonsistenz von ZustĂ€nden in unseren EntitĂ€ten zu vermeiden, mĂŒssen wir dies bei DomĂ€nenereignissen vermeiden. Lassen Sie uns dies anhand des folgenden Beispiels veranschaulichen: Das Produkt in unserem System verfĂŒgt ĂŒber eine Geolokalisierung mit LĂ€ngen- und Breitengrad, die in zwei verschiedenen Feldern der Produkttabelle des Produktservices gespeichert sind. Alle Produkte können "verschoben" werden, daher haben wir Domain-Ereignisse, um dieses Update zu prĂ€sentieren. Zuvor hatten wir zwei Ereignisse: product_latitude_updated und product_longitude_updated , was wenig Sinn machte, wenn Sie kein Turm auf einem Schachbrett waren. In diesem Fall sind die Ereignisse product_location_updated sinnvoller.oder product_moved .


Ein Turm ist eine Schachfigur. FrĂŒher hieß es Tour. Ein Turm kann sich nur vertikal oder horizontal durch eine beliebige Anzahl nicht besetzter Felder bewegen.

7. Fehlende Debugging-Tools
Wir bei letgo produzieren Tausende von Domain-Events pro Minute. All diese Ereignisse werden zu einer Ă€ußerst nĂŒtzlichen Ressource, um zu verstehen, was in unserem System geschieht, BenutzeraktivitĂ€ten zu registrieren oder sogar den Status eines Systems zu einem bestimmten Zeitpunkt mithilfe der Ereignissuche zu rekonstruieren. Wir mĂŒssen diese Ressource geschickt nutzen, und dafĂŒr benötigen wir Tools zum ÜberprĂŒfen und Debuggen unserer Ereignisse. Anfragen wie "Zeigen Sie mir alle Ereignisse, die John Doe in den letzten 3 Stunden generiert hat" können ebenfalls hilfreich sein, um Betrug aufzudecken. Zu diesem Zweck haben wir einige Tools fĂŒr ElasticSearch, Kibana und S3 entwickelt.

8. Fehlende EreignisĂŒberwachung
Wir können DomĂ€nenereignisse verwenden, um den Zustand des Systems zu testen. Wenn wir etwas bereitstellen (was je nach Dienst mehrmals tĂ€glich geschieht), benötigen wir Tools, um den korrekten Vorgang schnell zu ĂŒberprĂŒfen. Zum Beispiel, wenn wir eine neue Version des Produktservices in der Produktion bereitstellen und eine Verringerung der Anzahl von product_published- Ereignissen feststellen20%, man kann mit Sicherheit sagen, dass wir etwas kaputt gemacht haben. Wir verwenden derzeit InfluxDB, Grafana und Prometheus, um dies mit abgeleiteten Funktionen zu erreichen. Wenn Sie sich an den Verlauf der Mathematik erinnern, werden Sie verstehen, dass die Ableitung der Funktion f (x) am Punkt x gleich der Tangente des Tangentenwinkels ist, der an diesem Punkt zum Diagramm der Funktion gezogen wird. Wenn Sie eine Funktion zum Veröffentlichen der Geschwindigkeit eines bestimmten Ereignisses in einem Themenbereich haben und daraus eine Ableitung ableiten, werden die Spitzen dieser Funktion angezeigt und Sie können Benachrichtigungen basierend darauf festlegen. Mit diesen Benachrichtigungen können Sie AusdrĂŒcke wie "Warnen Sie mich, wenn wir 5 Minuten lang weniger als 200 Ereignisse pro Sekunde veröffentlichen" vermeiden und sich auf eine signifikante Änderung der Veröffentlichungsgeschwindigkeit konzentrieren.


Hier ist etwas Seltsames passiert ... Oder vielleicht ist es nur eine Marketingkampagne

9. Die Hoffnung, dass alles gut wird
Wir versuchen, nachhaltige Systeme zu schaffen und die Kosten fĂŒr ihre Wiederherstellung zu senken. Neben Infrastrukturproblemen und dem menschlichen Faktor ist der Verlust von Ereignissen eines der hĂ€ufigsten Dinge, die die Ereignisarchitektur beeinflussen können. Wir brauchen einen Plan, mit dem wir den korrekten Zustand des Systems wiederherstellen können, indem wir alle verlorenen Ereignisse erneut verarbeiten. Hier basiert unsere Strategie auf zwei Punkten:

  • : , « , », - , . letgo Data, Backend.
  • : - . , , , message bus . – , , . , user_registered Users, , MySQL, user_id . user_registered, , . , , - MySQL ( , 30 ). -, DynamoDB. , , , . , , , , .

10. Fehlende Dokumentation zu DomÀnenereignissen
Unsere DomĂ€nenereignisse sind zu unserer öffentlichen Schnittstelle fĂŒr alle Systeme im Backend geworden. So wie wir unsere REST-APIs dokumentieren, mĂŒssen wir auch DomĂ€nenereignisse dokumentieren. Jeder Mitarbeiter der Organisation sollte in der Lage sein, aktualisierte Dokumentationen fĂŒr jedes von jedem Dienst veröffentlichte DomĂ€nenereignis anzuzeigen. Wenn wir Schemata zum ÜberprĂŒfen von DomĂ€nenereignissen verwenden, können diese auch als Dokumentation verwendet werden.

11. Widerstand gegen den Konsum eigener Ereignisse
Sie dĂŒrfen und werden sogar dazu ermutigt, Ihre eigenen DomĂ€nenereignisse zu verwenden, um Projektionen in Ihrem System zu erstellen, die beispielsweise zum Lesen optimiert sind. Einige Teams widersetzten sich diesem Konzept, weil sie sich auf das Konzept des Konsums von Ereignissen anderer Leute beschrĂ€nkten.

Wir sehen uns auf dem Kurs!

All Articles