Zustände aus einer Ereignisfolge bilden
Event Sourcing: Wie Ereignisse den Zustand eines Systems bilden
81 Kilogramm Material sind verfügbar. Welche Einbuchungen, Reservierungen und Verbräuche erklären diesen Stand?
Event Sourcing macht Ereignisse zur Zustandsgrundlage
Fowler und Microsoft beschreiben Event Sourcing als Architekturentscheidung über die maßgebliche Zustandsgrundlage: Ein zusätzliches Änderungsprotokoll neben einer führenden Zustandstabelle genügt dafür nicht.[1][2]
Für eine Materialcharge stellt sich damit eine konkrete Rechenfrage: Welche Einbuchungen, Reservierungen und Verbräuche erklären die angezeigte verfügbare Menge? Das folgende fiktive Beispiel führt diese Rechnung aus. Es verwendet ein vereinfachtes Bestandsmodell.
Beispiel: Bestand einer Materialcharge
Im folgenden vereinfachten Beispiel beginnt die Projektion bei null Kilogramm. Die Einbuchung erhöht den Bestand auf 100 Kilogramm. Reservierungen vermindern nur die frei verfügbare Menge. Ein Verbrauch aus Auftrag A vermindert sowohl den Bestand als auch dessen Reservierung. Gespeichert werden folgende Ereignisse:
MaterialchargeEingebucht– 100 kgMaterialReserviert– 20 kg für Auftrag AMaterialVerbraucht– 18 kg in Aufgabe T-17ReservierungFreigegeben– verbleibende 2 kg aus Auftrag ABestandskorrekturErfasst– minus 1 kg nach bestätigter Differenz
Damit ergibt sich nach jedem Ereignis folgende Sicht. Verfügbar bedeutet hier Bestand minus Reservierung; weitere Sperren sind im Beispiel nicht enthalten.
| Ereignis | Bestand (kg) | Reserviert (kg) | Verfügbar (kg) |
|---|---|---|---|
| Einbuchung | 100 | 0 | 100 |
| Reservierung für A | 100 | 20 | 80 |
| Verbrauch aus A | 82 | 2 | 80 |
| Restreservierung freigeben | 82 | 0 | 82 |
| Bestandskorrektur | 81 | 0 | 81 |
Eine andere Ansicht kann nur Verbräuche nach Aufgaben darstellen. Eine dritte kann alle Korrekturen für die Qualitätssicherung bündeln. Alle Ansichten verwenden dieselben fachlichen Ereignisse, interpretieren sie aber für unterschiedliche Abfragen.
Das Beispiel zeigt zugleich eine Grenze. Wenn
MaterialVerbraucht keine Einheit, keine betroffene
Materialcharge oder keinen Bezug zur Aufgabe enthält, kann der aktuelle
Bestand vielleicht noch irgendwie berechnet werden. Die betriebliche
Bedeutung und spätere Nachvollziehbarkeit bleiben dennoch unzureichend.
Event Sourcing ersetzt keine gute Ereignissemantik.
Ereignis und Zustand
Ein Zustand beantwortet die Frage: Wie ist die Situation jetzt? Ein Ereignis beantwortet: Was ist geschehen? In einer klassischen zustandsorientierten Speicherung kann ein Datensatz beispielsweise enthalten:
Status = freigegeben
Ein ereignisorientiertes Modell könnte dagegen folgende Folge enthalten:
ChargeAngelegtPrüfungAngefordertNacharbeitGefordertNacharbeitAbgeschlossenChargeFreigegeben
Der aktuelle Status „freigegeben“ lässt sich aus dieser Reihenfolge ableiten. Gleichzeitig bleibt sichtbar, dass vor der Freigabe eine Rückgabe und Nacharbeit stattgefunden haben.
Die fachliche Bedeutung einzelner Prozessereignisse ist dabei eine eigene Frage. Ein Ereignis benötigt klare Semantik, Zeitbezug und Kontext. Event Sourcing beschreibt dagegen das Architekturprinzip, nach dem solche Ereignisse den Zustand des Systems bilden. Nicht jedes Prozessereignis muss Teil eines event-gesourcten Zustandsmodells sein, und nicht jedes technische Event besitzt automatisch betrieblichen Aussagewert.
Fachliche Ereignisse statt Statusänderungen
Ein gutes Ereignis benennt die Bedeutung einer Veränderung.
RestbestandAuf42Gesetzt beschreibt nur das Ergebnis.
ZweiEinheitenReserviert erklärt dagegen, was geschehen ist
und warum der Restbestand gesunken ist. Microsoft unterscheidet in
diesem Zusammenhang zustandsorientierte von absichtsorientierten
Ereignissen: Ereignisse mit fachlicher Bedeutung ermöglichen
aussagekräftigere Projektionen und spätere Auswertungen.[2]
Diese Benennung verlangt Disziplin. Ereignisse sollten Tatsachen in
der Vergangenheit beschreiben. Ein Ereignis ist weder ein Wunsch noch
ein Befehl. ChargeFreigeben wäre eine Aufforderung;
ChargeFreigegeben hält fest, dass die fachliche
Entscheidung bereits wirksam geworden ist.
Auch technische Oberbegriffe wie StatusGeändert oder
DatensatzAktualisiert sind meist zu schwach. Sie zwingen
spätere Leser und Systeme dazu, die Bedeutung aus alten und neuen
Feldwerten zu erraten. Fachliche Ereignisse machen dagegen ausdrücklich
sichtbar, welche Handlung, Entscheidung oder Wirkung eingetreten
ist.
Der Event Stream als geordnete Geschichte
Ein Event Stream ist die geordnete Folge der Ereignisse, die zu einer
bestimmten fachlichen Entität gehören. Das kann ein Auftrag, eine
Charge, eine Anlage oder ein anderer abgegrenzter Gegenstand sein. Die
Reihenfolge ist wesentlich: ChargeFreigegeben vor
PrüfungAbgeschlossen würde eine andere oder ungültige
Geschichte beschreiben als die umgekehrte Reihenfolge.
Zeitstempel allein reichen für diese Ordnung nicht immer aus. Mehrere Vorgänge können nahezu gleichzeitig eintreffen, Systeme können unterschiedlich genaue Uhren besitzen und Nachrichten können verzögert zugestellt werden. Deshalb werden Ereignisse innerhalb eines Streams häufig zusätzlich mit einer fortlaufenden Versions- oder Sequenznummer versehen.[2]
Diese Versionsnummer unterstützt auch die Kontrolle konkurrierender Änderungen. Wenn zwei Befehle denselben Ausgangsstand laden und beide ein neues Ereignis anhängen wollen, kann der Event Store erkennen, dass sich der Stream zwischenzeitlich verändert hat. Eine Operation wird dann abgelehnt, erneut bewertet oder kontrolliert wiederholt.[2]
Der Stream ist jedoch keine beliebige technische Nachrichtenfolge. Seine Grenzen müssen dem fachlichen Modell entsprechen. Werden zu viele unabhängige Objekte in einen einzigen Stream gezwungen, entstehen unnötige Konflikte. Werden zusammengehörige Änderungen zu stark verteilt, kann die fachliche Konsistenz verloren gehen.
Vom Ereignis zum aktuellen Zustand
Um den aktuellen Zustand zu bilden, beginnt das System mit einem
definierten Ausgangszustand und wendet die Ereignisse des Streams der
Reihe nach an. Aus ChargeAngelegt,
ChargeGesperrt und ChargeFreigegeben entsteht
beispielsweise eine aktuell freigegebene Charge.
Diese Wiederherstellung wird teuer, wenn ein Stream sehr lang ist. Deshalb können Snapshots eingesetzt werden. Ein Snapshot speichert den abgeleiteten Zustand an einem bestimmten Punkt. Bei der Rehydration lädt das System den jüngsten Snapshot und spielt nur die danach entstandenen Ereignisse ein. Der Snapshot ist eine Leistungsoptimierung; der Event Stream bleibt die maßgebliche Ereignisfolge.[2]
Neben dem internen Objektzustand können Projektionen oder materialisierte Ansichten aufgebaut werden. Sie übersetzen Ereignisse in Datenstrukturen, die für konkrete Abfragen optimiert sind: eine Bestandsübersicht, eine Liste offener Prüfungen oder den aktuellen Status aller Chargen. Solche Ansichten können jederzeit neu aufgebaut werden, sofern Ereignisse, Verarbeitungslogik und erforderlicher Kontext verfügbar bleiben.
Die Projektion darf deshalb nicht mit dem Ursprung verwechselt werden. Eine Dashboard-Zahl zeigt den berechneten aktuellen Stand. Der Event Stream erklärt, aus welchen Veränderungen dieser Stand entstanden ist.
Wiederholung ist nicht folgenlos
Die Möglichkeit, Ereignisse erneut abzuspielen, gehört zu den wichtigsten Eigenschaften von Event Sourcing. Sie ermöglicht den Neuaufbau von Zuständen und Projektionen. Replay ist jedoch nur sicher, wenn dieselben Ereignisse unter kontrollierten Bedingungen dasselbe interne Ergebnis erzeugen.
Externe Wirkungen dürfen bei einem Replay nicht unbemerkt erneut ausgelöst werden. Eine bereits versandte Nachricht, ausgeführte Zahlung oder an ein Fremdsystem gemeldete Freigabe soll beim Neuaufbau einer Projektion nicht ein zweites Mal erfolgen. Fowler hebt dieses Problem ausdrücklich hervor: Externe Systeme unterscheiden nicht automatisch zwischen realer Verarbeitung und Replay.[1]
Auch externe Abfragen können die Rekonstruktion verfälschen. Wenn die ursprüngliche Berechnung einen Wechselkurs, Messwert oder externen Status verwendete, muss beim späteren Replay derselbe historische Kontext verfügbar sein. Eine erneute Abfrage des heute geltenden Werts würde einen anderen Zustand erzeugen.
Ereignisverarbeiter müssen außerdem mit Mehrfachzustellung umgehen können. Microsoft weist darauf hin, dass Verbraucher dasselbe Ereignis mehrfach erhalten können. Die Verarbeitung sollte daher idempotent gestaltet sein: Dasselbe Ereignis darf bei wiederholter Zustellung nicht mehrfach auf den Zustand wirken.[2]
Eine Korrektur verändert die Rechnung
Im Bestandsbeispiel vermindert das letzte Ereignis den gebuchten Bestand um ein Kilogramm. Es ersetzt keinen früheren Verbrauch. Würde die Projektion stattdessen den Bestand auf einen absoluten Wert setzen, wäre das eine andere fachliche Regel. Ereignistyp und Nutzdaten müssen diese Wirkung eindeutig festlegen.
Im Append-only Log werden neue Einträge angefügt, statt frühere Einträge zu überschreiben. Bei Event Sourcing kommt die Auswertung hinzu: Auch ein kompensierendes Ereignis muss von der Zustandslogik verarbeitet werden. Die unverändert gespeicherte Ursprungshandlung und ihre spätere Berichtigung wirken gemeinsam auf das Ergebnis.[2]
Eine solche Historie benötigt außerdem ein geplantes Aufbewahrungs- und Löschkonzept. Welche Daten unmittelbar in Ereignissen stehen und welche kontrolliert referenziert werden, ist vor ihrer Erfassung festzulegen; aus dem Architekturprinzip folgt keine unbegrenzte Aufbewahrungserlaubnis.
Ereignisse verändern sich ebenfalls
Die fachliche Welt und die Software entwickeln sich weiter. Ein Ereignistyp kann später zusätzliche Angaben benötigen, eine Bezeichnung ändern oder anders interpretiert werden. Weil historische Ereignisse langfristig erhalten bleiben, muss das System alte und neue Schemaversionen gemeinsam verstehen können.
Mögliche Strategien sind Versionskennzeichen am Ereignis, Standardwerte für neue optionale Felder oder sogenannte Upcaster, die ältere Ereignisformen beim Lesen in eine aktuelle Form übersetzen. Microsoft nennt auch eine direkte Migration historischer Ereignisse, ordnet sie wegen des Bruchs der Unveränderlichkeit jedoch als letztes Mittel ein.[2]
Noch schwieriger sind Änderungen der Verarbeitungslogik. Ein alter Vorgang muss möglicherweise nach den damals geltenden Regeln verstanden werden, während neue Vorgänge einer geänderten Logik folgen. Deshalb genügt es nicht, nur die Ereignisdaten aufzubewahren. Für reproduzierbare historische Zustände müssen auch die jeweils maßgeblichen Regeln, Versionen und externen Bezugswerte berücksichtigt werden.
Diese Zusammenhänge berühren die Datenintegrität. Vollständige Ereignisfolgen verlieren ihren Wert, wenn ihre Semantik, Reihenfolge, Versionen oder Verarbeitung über den Lebenszyklus nicht verlässlich erhalten bleiben.
Event Sourcing und CQRS
Event Sourcing wird häufig mit Command Query Responsibility Segregation, kurz CQRS, kombiniert. CQRS trennt Modelle für schreibende Operationen von Modellen für Abfragen. Ereignisse können auf der Schreibseite entstehen und anschließend eine oder mehrere lesefreundliche Projektionen aktualisieren.[2][3]
Die Muster sind jedoch nicht identisch. Event Sourcing kann ohne CQRS verwendet werden. CQRS kann wiederum mit einer gewöhnlichen zustandsorientierten Datenhaltung umgesetzt werden. Die Kombination ist sinnvoll, wenn unterschiedliche Schreib- und Leseanforderungen tatsächlich den zusätzlichen Aufwand rechtfertigen.
Projektionen können gegenüber dem Event Store zeitlich verzögert sein. Dann liegt eventual consistency vor: Ein neu gespeichertes Ereignis ist bereits maßgeblich, während eine Leseansicht den neuen Stand noch nicht zeigt. Benutzeroberflächen und Folgeprozesse müssen diese Zwischenzeit bewusst behandeln.[2]
Zustandsbildung in der 420+-Architektur
Die Ledger-Architektur verbindet fachliche Ereignisse mit den daraus abgeleiteten Chargen- und Aufgabenständen. Für eine solche Zustandsbildung gehören die angewendete Regelversion und erforderliche Bezugswerte zum Zusammenhang der Ereignisfolge.
Was Event Sourcing nicht automatisch leistet
- Ein Event Store ist nicht automatisch ein regulatorischer Audit Trail. Zweck, Umfang, Darstellung und Prüfanforderungen sind gesondert zu bestimmen.
- Append-only bedeutet nicht manipulationssicher. Berechtigungen, technische Integritätskontrollen, Sicherung und unabhängige Verifikation bleiben eigenständige Maßnahmen.
- Ein gespeichertes Ereignis beweist keine fachliche Richtigkeit. Eine falsche Eingabe kann korrekt gespeichert und dennoch falsch sein.
- Event Sourcing ist nicht für jede Anwendung sinnvoll. Es erhöht die Anforderungen an Modellierung, Versionierung, Tests, Betrieb und Datenschutz.
- Ein Nachrichtenbroker ist nicht automatisch ein Event Store. Verteilung von Nachrichten und verlässliche Speicherung fachlicher Streams erfüllen unterschiedliche Aufgaben.[2]
- Event Sourcing ist nicht dasselbe wie ereignisgetriebene Architektur. Systeme können Ereignisse austauschen, ohne ihren Zustand aus einer dauerhaften Ereignisfolge zu bilden.
Der Neuaufbau ist die entscheidende Probe
Die Tabelle endet bei 81 Kilogramm verfügbarem Material. Ihr Erklärungswert liegt im Weg dorthin: Reservierung, Verbrauch und Aufhebung einer Restreservierung haben verschiedene Wirkungen. Eine bloße Folge von Zahlen würde diese Unterschiede verbergen.
Bei der Bewertung eines Event-Sourcing-Entwurfs sollte deshalb eine konkrete Ansicht neu aufgebaut werden. Stimmt das Ergebnis auch nach einer Korrektur, mit einer älteren Ereignisversion und bei mehrfach zugestellten Ereignissen? Bleiben externe Wirkungen währenddessen kontrolliert? Diese Fragen zeigen, ob aus der gespeicherten Folge tatsächlich ein verlässlich berechenbarer Zustand entsteht.
Primärquellen und weiterführende Literatur
- Martin Fowler, Event Sourcing, 12. Dezember 2005. Grundlagenbeitrag
- Microsoft Azure Architecture Center, Event Sourcing pattern. Architekturdokumentation
- Microsoft Azure Architecture Center, CQRS pattern. Architekturdokumentation