Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Statt nur den aktuellen Zustand der Daten in einer relationalen Datenbank zu speichern, speichern Sie die vollständige Reihe von Aktionen, die für ein Objekt in einem Nur-Anfügespeicher ausgeführt werden. Der Speicher fungiert als Datensatzsystem, mit dem Sie die Domänenobjekte materialisieren können. Dieser Ansatz kann die Auditierbarkeit und Schreibleistung in komplexen Systemen verbessern.
Von Bedeutung
Event Sourcing ist ein komplexes Muster, das erhebliche Kompromisse mit sich bringt. Es verändert die Art und Weise, wie Sie Daten speichern, Parallelität handhaben, Schemas weiterentwickeln und den Abfragestatus abfragen. Es ist kostspielig, zu oder von einer Event Sourcing-Lösung zu migrieren, und nachdem Sie das Muster angenommen haben, schränkt es zukünftige Entwurfsentscheidungen in den Teilen des Systems ein, die es verwenden. Übernehmen Sie Event Sourcing, wenn dessen Vorteile, wie Auditierbarkeit und historische Rekonstruktion, die Komplexität des Musters rechtfertigen. Für die meisten Systeme und die meisten Teile eines Systems reicht die herkömmliche Datenverwaltung aus.
Kontext und Problem
Die meisten Anwendungen arbeiten mit Daten. Die Anwendung speichert in der Regel den neuesten Status der Daten in einer relationalen Datenbank und fügt daten nach Bedarf ein oder aktualisiert sie. Beispielsweise liest eine Anwendung im herkömmlichen Erstellungs-, Lese-, Aktualisierungs- und Löschmodell (CRUD)-Modell Daten aus dem Speicher, ändert sie und aktualisiert den aktuellen Status der Daten mit den neuen Werten, in der Regel mithilfe von Transaktionen, die die Daten sperren.
Der CRUD-Ansatz ist für die meisten Szenarien einfach und schnell. In Hochlastsystemen stellt dieser Ansatz jedoch Herausforderungen dar:
Schreibkonflikt: Da Updates Lese-Änderungs-Schreib-Zyklen mit Zeilenebene-Sperren erfordern, beeinträchtigen gleichzeitige Schreibvorgänge an dasselbe Objekt die Leistung und führen unter Last zu einem Engpass.
Auditierbarkeit: CRUD-Systeme speichern nur den neuesten Status der Daten. Wenn Sie keinen Überwachungsmechanismus implementieren, mit dem die Details der einzelnen Vorgänge in einem separaten Protokoll aufgezeichnet werden, verlieren Sie den Datenverlauf.
Lösung
Das Event Sourcing-Muster definiert einen Ansatz zum Behandeln von Vorgängen mit Daten, die durch eine Abfolge von Ereignissen gesteuert werden. Jedes Ereignis wird in einem append-only-Datenspeicher aufgezeichnet. Der Anwendungscode löst Ereignisse aus, die jede Aktion beschreiben, die für das Objekt ausgeführt wird. Es sendet in der Regel Ereignisse an eine Warteschlange, auf die ein separater Prozess, ein Ereignishandler, lauscht, um die Ereignisse in einem Ereignisspeicher zu persistieren. Jedes Ereignis stellt eine logische Änderung des Objekts dar, wie zum Beispiel AddedItemToOrder oder OrderCanceled.
Die Ereignisse verbleiben in einem Ereignisspeicher, der als führendes System oder autoritative Datenquelle für den aktuellen Zustand der Daten dient. Zusätzliche Ereignishandler können auf bestimmte Ereignisse lauschen und bei Bedarf Maßnahmen ergreifen. Beispielsweise können Verbraucher Aufgaben initiieren, die Vorgänge in den Ereignissen auf andere Systeme anwenden oder andere zugeordnete Aktionen ausführen, die zum Abschließen des Vorgangs erforderlich sind. Der Anwendungscode, der die Ereignisse generiert, wird von den Systemen entkoppelt, die die Ereignisse abonnieren.
Jede Entität in einem Ereignisquellsystem verfügt über einen eigenen Ereignisdatenstrom, bei dem es sich um die sortierte Abfolge von Ereignissen handelt, die jede Änderung in dieser Entität aufzeichnet. Zu jedem Zeitpunkt können Anwendungen den Verlauf von Ereignissen lesen. Anwendungen leiten den aktuellen Status einer Entität ab, indem alle Ereignisse im Datenstrom wiedergegeben werden. Dieser Prozess wird als Rehydratation bezeichnet. Sie kann bei Bedarf auftreten, wenn die Anwendung eine Anforderung verarbeitet.
Anwendungen implementieren in der Regel materialisierte Ansichten , da es kostspielig ist, Ereignisse zu lesen und wiederzuverwenden. Materialisierte Ansichten sind schreibgeschützte Projektionen des Ereignisspeichers, die für die Abfrage optimiert sind. Beispielsweise kann ein System eine materialisierte Ansicht aller Kundenbestellungen verwalten, die zum Auffüllen der Benutzeroberfläche verwendet werden. Wenn die Anwendung neue Bestellungen hinzufügt, Elemente in der Bestellung hinzufügt oder entfernt oder Versandinformationen hinzufügt, löst die Anwendung Ereignisse aus, und ein Handler aktualisiert die materialisierte Ansicht.
Das folgende Diagramm zeigt eine Übersicht über dieses Muster in Kombination mit dem CQRS-Muster (Command Query Responsibility Segregation). Die Präsentationsebene liest aus einem separaten schreibgeschützten Speicher und schreibt Befehle an Befehlshandler. Die Befehlshandler rufen den Ereignisdatenstrom der Entität aus dem Ereignisspeicher ab, führen Geschäftslogik aus und übertragen neue Ereignisse in eine Warteschlange. Ereignishandler nutzen Ereignisse aus der Warteschlange und schreiben Ereignisse in den Ereignisspeicher, aktualisieren den schreibgeschützten Speicher oder integrieren in externe Systeme.
Laden Sie eine Visio-Datei dieser Architektur herunter.
Arbeitsablauf
Der folgende Workflow entspricht dem vorherigen Diagramm:
Die Präsentationsebene ruft ein Objekt auf, das aus einem schreibgeschützten Speicher gelesen wird. Sie verwendet die zurückgegebenen Daten, um die Benutzeroberfläche aufzufüllen.
Auf der Präsentationsebene werden Befehlshandler aufgerufen, um Aktionen wie das Erstellen eines Einkaufswagens oder das Hinzufügen eines Elements zum Warenkorb auszuführen.
Der Befehlshandler lädt die Entität, indem der Ereignisdatenstrom aus dem Ereignisspeicher abgerufen wird. Beispielsweise kann es alle Einkaufswagenereignisse abrufen. Es spielt diese Ereignisse auf die Entität ab, um ihren aktuellen Zustand zu rekonstruieren, bevor eine neue Aktion erfolgt.
Die Geschäftslogik wird ausgeführt, und Ereignisse werden ausgelöst. In den meisten Implementierungen werden die Ereignisse in eine Warteschlange oder ein Thema verschoben, um die Ereignishersteller und Ereigniskonsumenten zu entkoppeln.
Ereignishandler lauschen auf bestimmte Ereignisse und ergreifen die entsprechende Aktion für diesen Handler. In diesem Beispiel führen die Ereignishandler die folgenden Aktionen aus:
Die Ereignisse in den Ereignisspeicher schreiben
Aktualisieren eines schreibgeschützten Speichers, der für Abfragen optimiert ist
Integration in externe Systeme
Mustervorteile
Das Ereignissourcingmuster bietet folgende Vorteile:
Ereignisse sind unveränderlich, und Sie können sie mithilfe eines Nur-Anfügevorgangs speichern. Die Benutzeroberfläche, der Workflow oder der Prozess, der ein Ereignis initiiert, kann fortgesetzt werden, und Aufgaben, die die Ereignisse behandeln, können im Hintergrund ausgeführt werden. Der Schreibdurchsatz verbessert sich, insbesondere für die Präsentationsebene, da Nur-Anfüge-Schreibvorgänge die Sperrkonkurrenz auf Zeilenebene vermeiden, die In-Place-Update-Systeme erzeugen.
Ereignisse sind einfache Objekte, die eine Aktion beschreiben, die zusammen mit allen zugeordneten Daten auftritt, die erforderlich sind, um die Aktion zu beschreiben, die das Ereignis darstellt. Ereignisse aktualisieren einen Datenspeicher nicht direkt. Ereignishandler erfassen und verarbeiten aufgezeichnete Ereignisse, wenn ein Handler verfügbar ist, und das System kann die Last verarbeiten. Verwenden Sie Ereignisse, um die Implementierung und Verwaltung zu vereinfachen.
Ereignisse haben in der Regel eine Bedeutung für einen Domänenexperten, während die objektrelationale Unverträglichkeit (Object-Relational Impedance Mismatch) dazu führen kann, dass komplexe Datenbanktabellen schwer zu verstehen sind. Tabellen sind künstliche Konstrukte, die den aktuellen Zustand des Systems darstellen, nicht die Ereignisse, die auftreten.
Ereignissourcing kann verhindern, dass gleichzeitige Updates Konflikte verursachen, weil Objekte nicht direkt im Datenspeicher aktualisiert werden müssen. Befehlshandler rehydrieren eine Entität aus ihrem Ereignisdatenstrom, um Geschäftsregeln durchzusetzen, bevor sie neue Ereignisse hinzufügen. Zwei Handler, die die gleiche Entität gleichzeitig laden, können so auf denselben Zustand zugreifen und bearbeiten.
Jeder Mitarbeiter sieht beispielsweise fünf verbleibende Plätze, und beide Mitarbeiter können eine Reservierung akzeptieren. Ereignisspeicher behandeln dieses Szenario mithilfe des optimistischen Parallelitätssteuerelements und ablehnen eine Anfüge, wenn der Datenstrom seit dem Lesen geändert wurde. Bei Zurückweisung lädt der Handler die Entität neu, bewertet sie erneut und versucht es erneut.
Der Nur-Anfüge-Ereignisspeicher stellt einen Überwachungspfad bereit, mit dem Anwendungen Aktionen überwachen können, die für einen Datenspeicher ausgeführt werden. Er kann den aktuellen Zustand als materialisierte Ansichten oder Projektionen neu generieren, indem die Ereignisse jederzeit wiedergegeben werden, und es kann dazu beitragen, das System zu testen und zu debuggen.
Die Anforderung, ausgleichende Ereignisse zum Abbrechen von Änderungen zu verwenden, kann einen Verlauf von umgekehrten Änderungen bereitstellen. Wenn das Modell nur den aktuellen Zustand speichert, ist dieser Verlauf nicht vorhanden. Sie können auch die Liste der Ereignisse verwenden, um die Anwendungsleistung zu analysieren, Benutzerverhaltenstrends zu erkennen und andere nützliche Geschäftsinformationen zu erhalten.
Die Befehlshandler lösen Ereignisse aus, und Aufgaben führen Vorgänge als Reaktion auf diese Ereignisse aus. Diese Abkopplung der Tasks von den Ereignissen sorgt für Flexibilität und Erweiterbarkeit. Aufgaben kennen den Typ des Ereignisses und die Ereignisdaten, aber nicht über den Vorgang, der das Ereignis auslöst.
Mehrere Aufgaben können jedes Ereignis verarbeiten, sodass sie problemlos in andere Dienste und Systeme integriert werden können, die nur auf neue Ereignisse lauschen, die der Ereignisspeicher auslöst. Die Ereignisquellen sind typischerweise auf niedriger Ebene, und es kann notwendig sein, stattdessen spezifische Integrationsereignisse zu generieren.
Tipp
Die Ereignisbeschaffung wird häufig mit dem CQRS-Muster kombiniert, indem die Datenverwaltungsaufgaben als Reaktion auf die Ereignisse ausgeführt und Ansichten aus den gespeicherten Ereignissen materialisiert werden. Verwenden Sie diese Kombination, um Lese- und Schreibvorgänge unabhängig zu skalieren, da die Erfassung von append-only Ereignissen und abfrageoptimierte Projektionen separat ausgeführt werden.
Probleme und Überlegungen
Berücksichtigen Sie die folgenden Punkte, wenn Sie sich für die Implementierung dieses Musters entscheiden:
Ereignisdesign: Entwerfen Sie Ereignisse, um die Geschäftsabsicht hinter jeder Änderung zusätzlich zum resultierenden Zustand zu erfassen. Beispielsweise ist im Sitzreservierungssystem ein Ereignis, bei dem zwei Sitze reserviert wurden, wertvoller als ein Ereignis, bei dem die verbleibenden Sitze auf 42 geändert wurden. Das erste Ereignis teilt Ihnen mit, was passiert ist. Das zweite Ereignis informiert Sie nur über den resultierenden Zustand. Zustandsorientierte Ereignisse reduzieren den Ereignisspeicher auf ein Änderungsprotokoll, das keine geschäftliche Bedeutung hat. Absichtsorientierte Ereignisse bieten detailliertere Projektionen, aussagekräftige Überwachungspfade und die Flexibilität, neue Lesemodelle aus historischen Ereignissen zu erstellen, ohne die Schreibumgebung ändern zu müssen.
Mögliche Konsistenz: Das System ist letztendlich nur konsistent, wenn es materialisierte Ansichten erstellt oder Projektionen von Daten durch wiedergebende Ereignisse generiert. Zwischen dem Zeitpunkt, zu dem eine Anwendung eine Anforderung bearbeitet und Ereignisse zum Ereignisspeicher hinzufügt, dem Veröffentlichen der Ereignisse und dem Bearbeiten der Ereignisse durch die Verbraucher besteht eine Verzögerung. Während dieses Zeitraums können neue Ereignisse, die weitere Änderungen an Entitäten beschreiben, im Ereignisspeicher eingehen. Stellen Sie sicher, dass Ihre Kunden verstehen, dass Daten letztendlich konsistent sind und dass das System für die spätere Konsistenz in diesen Szenarien konzipiert ist.
Versionsverwaltungsereignisse: Der Ereignisspeicher ist die permanente Informationsquelle, sodass Sie die Ereignisdaten niemals aktualisieren sollten. Die einzige Möglichkeit, eine Entität zu aktualisieren oder eine Änderung rückgängig zu machen, besteht darin, dem Ereignisspeicher ein Ausgleichsereignis hinzuzufügen. Ein Ausgleichsereignis ist ein neues Ereignis, das die Auswirkung eines vorherigen Ereignisses umkehrt oder korrigiert. Beispielsweise entschädigt ein
ReservationCanceledEreignis ein vorherigesSeatsReservedEreignis. Das ursprüngliche Ereignis verbleibt im Datenstrom, und das ausgleichende Ereignis zeichnet auf, dass es rückgängig gemacht wurde.Diese Unveränderlichkeit bedeutet auch, dass diese Ereignisse im Speicher beibehalten werden, wenn ein Fehler falsche Ereignisse erzeugt. Durch das Beheben des Fehlers im Anwendungscode werden die historischen Ereignisse nicht behoben, sodass Sie möglicherweise auch Ausgleichsereignisse oder Upcaster benötigen, um die fehlerhaften Daten während der Wiedergabe zu behandeln. Wenn sich das Schema (anstelle der Daten) der gespeicherten Ereignisse ändern muss, z. B. während einer Migration, kann es schwierig sein, vorhandene Ereignisse im Speicher mit der neuen Version zu kombinieren.
Sie können die folgenden Strategien einzeln oder in Kombination verwenden:
Tolerante Deserialisierung: Entwerfen Sie Ereignis-Consumer, um unbekannte Felder zu ignorieren und Standardwerte für fehlende Felder zu verwenden. Dieser Ansatz behandelt additive, nicht-brechende Änderungen, z. B. das Hinzufügen eines optionalen Felds, ohne dass eine Transformation gespeicherter Ereignisse erforderlich ist.
Ereignisversionsverwaltung: Fügen Sie einen Versionsbezeichner in jedes Ereignis ein, entweder als Metadaten im Ereignisumschlag oder als Teil des Ereignistypnamens. Verbraucher verwenden die Version, um die entsprechende Verarbeitungslogik auszuwählen.
Upcasting: Registrieren Sie Transformationsfunktionen, die ältere Ereignisschemas während der Deserialisierung in das aktuelle Schema konvertieren. Sie können Upcaster verketten, damit der Anwendungscode nur die neueste Version verarbeiten muss. Die gespeicherten Ereignisse bleiben unverändert, wodurch die Unveränderlichkeit erhalten bleibt.
Direkte Migration: Schreiben Sie historische Ereignisse direkt im Ereignisspeicher in das neue Schema um. Dieser Ansatz bricht die Unveränderlichkeit und sollte nur als letztes Mittel eingesetzt werden, da er den Prüfpfad untergräbt.
Ereignisbestellung: Mehrere Threadanwendungen und mehrere Instanzen von Anwendungen können Ereignisse im Ereignisspeicher speichern. Die Konsistenz von Ereignissen im Ereignisspeicher und die Reihenfolge von Ereignissen, die sich auf den aktuellen Zustand einer bestimmten Entität auswirken, sind von entscheidender Bedeutung. Wenn Sie jedem Ereignis einen Zeitstempel hinzufügen, können Sie Probleme vermeiden. Eine weitere gängige Vorgehensweise besteht darin, jedes Ereignis zu kommentieren, das aus einer Anforderung mit einem inkrementellen Bezeichner resultiert. Wenn zwei Aktionen versuchen, gleichzeitig Ereignisse für die gleiche Entität hinzuzufügen, kann der Ereignisspeicher ein Ereignis ablehnen, das mit einem vorhandenen Entitätsbezeichner und einem vorhandenen Ereignisbezeichner übereinstimmt.
Ereignisabfrage: Es gibt keinen Standardansatz oder vorhandene Mechanismen, z. B. SQL-Abfragen, zum Lesen von Ereignissen zum Abrufen von Informationen. Die einzigen Daten, die Sie extrahieren können, sind ein Datenstrom von Ereignissen, indem Sie einen Ereignisbezeichner als Kriterien verwenden. Die Ereignis-ID lässt sich in der Regel einzelnen Entitäten zuordnen. Sie können den aktuellen Status einer Entität nur ermitteln, indem Sie alle Ereignisse wiedergeben, die mit dem ursprünglichen Zustand dieser Entität zusammenhängen.
Ereignisspeicheroptionen: Ein Ereignisspeicher kann eine zweckorientierte Datenbank sein, die für Nur-Anfüge-Ereignisdatenströme vorgesehen ist, oder eine allgemeine relationale oder Dokumentdatenbank mit einer Nur-Anfüge-Tabelle.
Zweckorientierte Ereignisspeicher bieten integrierte Unterstützung für Aufgaben wie das Lesen eines Datenstroms nach Entität, optimistische Parallelität und Momentaufnahmen.
Relationale Datenbanken sind vertraut und allgemein verfügbar, erfordern jedoch, dass Sie diese Verhaltensweisen selbst erstellen.
Da jede Entität über einen eigenen unabhängigen Ereignisdatenstrom verfügt, partitionieren Ereignisspeicher automatisch nach Entitäts-ID, was bei Bedarf die horizontale Skalierung oder das Sharding vereinfacht.
Von Bedeutung
Verwechseln Sie einen Ereignisspeicher nicht mit einem Ereignisstream-Nachrichtenbroker. Nachrichtenbroker wie Apache Kafka verfügen in der Regel über fehlende Datenstromabfragen pro Entität und optimistische Parallelität. Sie eignen sich gut als Verteilungsschicht, um Ereignisse an Projektionen und externe Datenverbraucher zu verteilen, aber sie sind kein Ersatz für einen Ereignisspeicher.
Entitätsstatus neu erstellen: Die Länge jedes Ereignisdatenstroms wirkt sich auf die Verwaltung und Aktualisierung des Systems aus. Wenn die Datenströme groß sind, wird die Wiedergabe jedes Ereignisses zum Rehydratieren einer Entität sowohl in der Zeit als auch bei der Berechnung kostspielig. Um diese Kosten zu verringern, erstellen Sie Momentaufnahmen in bestimmten Intervallen, z. B. alle N-Ereignisse . Eine Momentaufnahme ist eine serialisierte Darstellung des Status der Entität an einem bestimmten Punkt im Ereignisdatenstrom. Wenn Sie die Entität rehydratisieren möchten, laden Sie die aktuellste Momentaufnahme und spielen Sie nur die Ereignisse ab, die danach auftreten, anstatt den gesamten Datenstrom von Anfang an erneut abzuspielen. Wenn Sie eine Momentaufnahmehäufigkeit auswählen, ausgleichen Sie die Speicherkosten von Momentaufnahmen mit der Zeit, die während der Rehydratation gespeichert wurde.
Hinweis
Momentaufnahmen sind eine Optimierung, kein Ersatz für den Ereignisdatenstrom. Der Ereignisdatenstrom bleibt die Quelle der Wahrheit, und Sie können Momentaufnahmen jederzeit neu generieren.
Konfliktbehandlung: Das optimistische Parallelitätssteuerelement verhindert widersprüchliche Schreibvorgänge in denselben Ereignisdatenstrom, die Anwendung muss jedoch weiterhin Konflikte behandeln, die sich über mehrere Entitäten erstrecken. Beispielsweise kann ein Ereignis, das eine Verringerung des Lagerbestands angibt, im Datenspeicher erfasst werden, während ein Kunde diesen Artikel bestellt. Entwerfen Sie das System so, dass diese Situationen in Einklang gebracht werden, z. B. durch Beratung des Kunden oder durch Erstellen einer Rückbestellung.
Anforderungen an die Idempotenz: Die Ereignisübermittlung an Verbraucher erfolgt in der Regel mindestens einmal, sodass Verbraucher dasselbe Ereignis mehrmals empfangen können. Ereignishandler müssen idempotent sein, damit die Verarbeitung eines doppelten Ereignisses das Ergebnis nicht ändert. Wenn zum Beispiel mehrere Instanzen eines Verbraucherprozesses, der Buchungsereignisse verarbeitet, verwendet werden, um die Anzahl verfügbarer Sitzplätze zu verwalten, muss ein dupliziertes Reservierungsereignis nur zu einer Verringerung der Anzahl führen. Ohne Idempotenz driften Projektionen vom Ereignisdatenstrom, und Nebenwirkungen wie Zahlungen oder Benachrichtigungen werden mehr als einmal ausgelöst. Verfolgen Sie die letzte verarbeitete Ereignissequenznummer für jeden Verbraucher, und überspringen Sie Duplikate, oder entwerfen Sie Zustandsmutationen, die inhärent sicher zu wiederholen sind.
Zirkellogik: Berücksichtigen Sie Szenarien, in denen die Verarbeitung eines Ereignisses die Erstellung eines oder mehrerer neuer Ereignisse erfordert. Diese Sequenz kann zu einer endlosen Schleife führen.
Testen: Ein spezifischer Testansatz eignet sich am besten für ereignisquellenbasierte Systeme. Richten Sie vergangene Ereignisse ein, geben Sie einen Befehl aus, und bestätigen Sie die neuen Ereignisse, die erstellt wurden. Dieser given-when-then-Ansatz testet Geschäftslogik ohne Datenbanken, Warteschlangen oder Projektionen. Sie benötigen aber auch Integrationstests für Projektionen, Idempotenzverhalten und Schemaentwicklungspfade, wodurch im Vergleich zu CRUD-Systemen eine größere Testoberfläche hinzugefügt wird.
Personenbezogene Daten und gesetzliche Compliance: Der anfügende, unveränderliche Charakter eines Ereignisspeichers steht im Widerspruch zu Datenschutzbestimmungen, die die Löschung personenbezogener Daten erfordern, wie etwa den Recht auf Vergessenwerden-Gesetzen. Durch das Löschen von Ereignissen wird die Datenstromintegrität sofort unterbrochen. Entwerfen Sie also von Anfang an diese Spannung.
Ein allgemeiner Ansatz besteht darin, personenbezogene Daten außerhalb des Ereignisspeichers zu speichern und anhand des Bezeichners in Ereignissen darauf zu verweisen. Mit diesem Ansatz kann das Löschen unabhängig voneinander erfolgen, ohne dass sich dies auf den Ereignisdatenstrom auswirkt.
Wenn Sie personenbezogene Daten nicht von Ereignissen trennen können, verwenden Sie Krypto-Shredding. Verschlüsseln Sie personenbezogene Daten in Ereignissen mithilfe eines Betreffschlüssels. Löschen Sie den Schlüssel, um die Daten nicht wiederherstellbar zu machen und die Ereignisstruktur intakt zu lassen. Dieser Ansatz erhöht den Verschlüsselungsaufwand für jeden Lese- und Schreibzugriff und erfordert eine robuste Schlüsselverwaltung.
Wann Sie dieses Muster verwenden sollten
Verwenden Sie dieses Muster in folgenden Fällen:
Sie möchten Absicht, Zweck oder Grund in den Daten erfassen. Beispielsweise können Sie Änderungen an einer Kundenentität als eine Reihe bestimmter Ereignistypen erfassen, z. B. "Verschoben nach Hause", " Geschlossenes Konto" oder "Verstorbene".
Sie müssen Widersprüchliche Aktualisierungen von Daten minimieren oder vollständig vermeiden.
Sie möchten Ereignisse aufzeichnen, die auftreten, um sie wiederzugeben, um den Status eines Systems wiederherzustellen, Änderungen zurückzusetzen oder einen Verlauf und ein Überwachungsprotokoll beizubehalten. Wenn beispielsweise eine Aufgabe aus mehreren Schritten besteht, müssen Sie möglicherweise Aktionen ausführen, um Updates rückgängig zu machen und dann einige Schritte zu wiederholen, um die Daten wieder in einen konsistenten Zustand zu versetzen.
Die Anwendung verwendet bereits Ereignisse als natürliches Merkmal ihres Betriebs, und die Ereignisbeschaffung erfordert wenig zusätzliche Entwicklungs- oder Implementierungsaufwand.
Sie müssen den Vorgang zum Eingeben oder Aktualisieren von Daten von den aufgaben entkoppeln, die zum Anwenden dieser Aktionen erforderlich sind. Diese Änderung kann sein, um die Ui-Leistung zu verbessern oder Ereignisse an andere Listener zu verteilen, die reagieren, wenn die Ereignisse auftreten. Sie können z. B. ein Lohnabrechnungssystem mit einer Kostenübermittlungswebsite integrieren. Sowohl die Website als auch das Lohnbuchhaltungssystem empfangen Ereignisse, die der Ereignisspeicher generiert, als Reaktion auf aktualisierte Daten auf der Website.
Sie möchten die Flexibilität, das Format der materialisierten Modelle und Entitätsdaten zu ändern, wenn sich die Anforderungen ändern, oder wenn Sie CQRS verwenden und ein Lesemodell oder die Ansichten anpassen müssen, die die Daten verfügbar machen.
Sie verwenden CQRS und letztendliche Konsistenz ist akzeptabel, während ein Lesemodell aktualisiert wird, oder die Rehydrierung von Entitäten und Daten aus einem Ereignisdatenstrom führt zu einer akzeptablen Leistungsminderung.
Dieses Muster ist möglicherweise nicht geeignet, wenn:
Systeme verfügen über einfache CRUD-Vorgänge, die keine Auditierbarkeit, Wiedergabe oder historische Wiederherstellung des Zustands erfordern. Der Betriebsaufwand eines Ereignisspeichers ist nicht gerechtfertigt, wenn die einzige Anforderung darin besteht, aktuelle Lese- und Schreibvorgänge durchzuführen.
Prototypen, minimal lebensfähige Produkte (MVPs) oder Systeme haben eine kurze erwartete Lebensdauer. Die Vorabinvestitionen in Ereignisdesign, Schemaentwicklungsstrategie und Projektionsinfrastruktur erzielen in diesen Szenarien selten eine Rendite.
Systeme erfordern Konsistenz- und Echtzeitaktualisierungen der Ansichten der Daten. Die letztendliche Konsistenz zwischen dem Ereignisspeicher und den Projektionen ist ein wesentlicher Bestandteil des Event Sourcing.
Domänen, in denen Daten hauptsächlich statisch oder referenziert sind, z. B. Nachschlagetabellen oder Kataloge. Diese Art von Daten ändert sich selten und profitiert nicht vom Änderungsverlauf.
Teams verfügen nicht über Erfahrung in ereignisgesteuerten Architekturen. Die Ereignisbeschaffung ändert, wie Sie ein System testen, debuggen und betreiben. Die Einführung ohne das grundlegende Wissen erhöht das Risiko von Antipatternen, die teuer umzukehren sind.
Tipp
Event Sourcing muss keine Alles-oder-Nichts-Entscheidung für das gesamte System sein. Wenden Sie es selektiv auf die Teile Ihres Systems an, von denen es am meisten profitiert, z. B. eine Zahlungsbuchhaltung oder eine Auftragsverarbeitungspipeline. Verwenden Sie herkömmliche CRUD für Teile, wenn die Komplexität nicht gerechtfertigt ist, z. B. Benutzerprofilverwaltung oder Anwendungskonfiguration.
Workloadentwurf
Bewerten Sie, wie Sie das Event Sourcing-Muster im Design einer Workload verwenden, um die Ziele und Prinzipien zu erfüllen, die in den Azure Well-Architected Framework-Säulen behandelt werden. Die folgende Tabelle enthält Anleitungen dazu, wie dieses Muster die Ziele jeder Säule unterstützt.
| Säule | So unterstützt dieses Muster die Säulenziele |
|---|---|
| Zuverlässigkeitsentwurfsentscheidungen helfen Ihrer Arbeitsauslastung, ausfallsicher zu werden und sicherzustellen, dass sie nach auftreten eines Fehlers wieder in einen voll funktionsfähigen Zustand versetzt wird. | Dieses Muster kann die Zustandsrekonstruktion erleichtern, wenn Sie Zustandsspeicher wiederherstellen müssen, da Sie einen Verlauf von Änderungen in komplexen Geschäftsprozessen erfassen. - Datenpartitionierung - RE:09 Notfallwiederherstellung |
| Performance Efficiency hilft Ihrem Workload durch Optimierungen bei Skalierung, Daten und Code, die Anforderungen effizient zu erfüllen . | Dieses Muster, in der Regel in Kombination mit CQRS, einem geeigneten Domänendesign und strategischem Snapshotting, kann die Leistung durch atomare Anfügevorgänge und die Vermeidung von Datenbanksperrungen für Schreib- und Lesevorgänge verbessern. - PE:08 Datenleistung |
Wenn dieses Muster Kompromisse innerhalb einer Säule einführt, sollten Sie sie gegen die Ziele der anderen Säulen berücksichtigen.
Beispiel
Ein Konferenzverwaltungssystem muss die Anzahl der abgeschlossenen Buchungen für eine Konferenz nachvollziehen können. Durch die Nachverfolgung dieser Nummer kann überprüft werden, ob verfügbare Plätze vorhanden sind, wenn ein potenzieller Teilnehmer versucht, eine Buchung vorzunehmen. Das System kann die Gesamtanzahl der Buchungen für eine Konferenz auf mindestens zwei Arten speichern:
Das System kann Informationen über die Gesamtzahl der Buchungen als separate Entität in einer Datenbank speichern, die Buchungsinformationen enthält. Wenn Teilnehmer Buchungen vornehmen oder stornieren, erhöht oder verringert das System diese Zahl. Dieser Ansatz ist theoretisch einfach, kann aber Skalierbarkeitsprobleme verursachen, wenn eine große Anzahl von Teilnehmern versuchen, Plätze in kurzer Zeit zu buchen. Dieser Anstieg tritt z. B. in der Regel am letzten Tag auf, bevor der Buchungszeitraum geschlossen wird.
Das System kann Informationen zu Buchungen und Stornierungen als Ereignisse speichern, die in einem Ereignisspeicher gespeichert werden. Sie berechnet die Anzahl der verfügbaren Sitzplätze, indem diese Ereignisse wiedergegeben werden. Dieser Ansatz kann aufgrund der Unveränderlichkeit von Ereignissen skalierbarer werden. Das System muss nur Daten aus dem Ereignisspeicher lesen oder Daten an den Ereignisspeicher anfügen. Es ändert niemals Ereignisinformationen zu Buchungen und Stornierungen.
Das folgende Diagramm zeigt, wie Sie Event Sourcing verwenden können, um das Subsystem für die Sitzplatzreservierung des Konferenzmanagementsystems zu implementieren.
Laden Sie eine Visio-Datei dieser Architektur herunter.
Arbeitsablauf
Der folgende Workflow entspricht dem vorherigen Diagramm:
Die Benutzeroberfläche gibt einen Befehl aus, um Platz für zwei Teilnehmer zu reservieren. Ein separater Befehlshandler behandelt den Befehl. Der Befehlshandler ist eine Logik, die von der Benutzeroberfläche entkoppelt wird und für die Behandlung von Anforderungen verantwortlich ist, die als Befehle bereitgestellt werden.
Das System erstellt eine Entität, die Informationen zu allen Reservierungen für die Konferenz enthält, indem die Ereignisse wiedergegeben werden, die Buchungen und Stornierungen beschreiben. Diese Entität wird
SeatAvailabilitygenannt und ist in einem Domänenmodell enthalten, das Methoden zum Abfragen und Ändern der Daten in der Entität bereitstellt.Tipp
Berücksichtigen Sie Optimierungen wie Momentaufnahmen, damit Sie nicht die vollständige Liste der Ereignisse wiedergeben müssen, um den aktuellen Status der Entität abzurufen. Momentaufnahmen verwalten auch eine zwischengespeicherte Kopie der Entität im Arbeitsspeicher.
Der Befehlshandler ruft eine Methode auf, die das Domänenmodell verfügbar macht, um die Reservierungen vorzunehmen.
Die
SeatAvailabilityEntität löst ein Ereignis aus, das die Anzahl der reservierten Sitzplätze enthält. Wenn die Entität das nächste Mal Ereignisse anwendet, benutzt sie alle Reservierungen, um die Anzahl der verbleibenden Sitzplätze zu berechnen.Das System fügt das neue Ereignis an die Liste der Ereignisse im Ereignisspeicher an.
Wenn ein Benutzer einen Platz storniert, folgt das System einem ähnlichen Prozess, indem der Befehlshandler einen Befehl ausgibt, der ein Sitzstornierungsereignis generiert und es an den Ereignisspeicher anhängt.
Das System kann einen vollständigen Verlauf oder Überwachungspfad der Buchungen und Stornierungen für eine Konferenz mithilfe eines Veranstaltungsspeichers bereitstellen. Die Ereignisse im Ereignisspeicher sind die genaue Aufzeichnung. Sie müssen Entitäten auf keine andere Weise beibehalten, da das System die Ereignisse problemlos wiedergeben und den Zustand zu einem beliebigen Zeitpunkt wiederherstellen kann.
Nächster Schritt
- CQRS-Muster: Der Schreibspeicher, der die permanente Informationsquelle für eine CQRS-Implementierung bereitstellt, basiert in der Regel auf einer Implementierung des Event Sourcing-Musters. Das Muster trennt die Vorgänge, die Daten in einer Anwendung lesen, von den Vorgängen, die Daten mithilfe separater Schnittstellen aktualisieren.
Communityressourcen
Event Sourcing, von Martin Fowler: Die ursprüngliche Beschreibung des Musters, das das grundlegende Vokabular begründet hat.
CQRS Documents (PDF), von Greg Young: Die endgültige Ressource zum Thema Event Sourcing und CQRS vom Praktiker, der beide Muster formalisiert hat.
Verwandte Ressourcen
Die folgenden Muster und Anleitungen können auch relevant sein, wenn Sie dieses Muster implementieren:
Materialisiertes Ansichtsmuster: Der Datenspeicher, den Sie in einem Ereignisbeschaffungssystem verwenden, eignet sich in der Regel nicht für eine effiziente Abfrage. Stattdessen besteht ein allgemeiner Ansatz darin, vorab aufgefüllte Ansichten der Daten in regelmäßigen Abständen zu generieren oder wenn sich die Daten ändern.
Ausgleichs-Transaktionsmuster: Das System aktualisiert keine vorhandenen Daten in einem Ereignis-Quellspeicher. Stattdessen werden neue Einträge hinzugefügt, die den Status von Entitäten auf die neuen Werte übertragen. Um eine Änderung rückgängig zu machen, verwendet sie ausgleichende Einträge, da sie die vorherige Änderung nicht rückgängig machen kann. Im Artikel "Kompensationstransaktion" wird beschrieben, wie Sie die Arbeit rückgängig machen, die ein vorheriger Vorgang ausgeführt hat.
Domänenanalyse für Microservices: In Systemen, die domänengesteuertes Design (DDD) verwenden, ist die Entität, die einen Ereignisdatenstrom besitzt, in der Regel ein Aggregat, eine Konsistenzgrenze, die Befehle empfängt, Geschäftsregeln erzwingt und Ereignisse ausgibt.