Event Sourcing
Event Sourcing (ES) ist ein Verfahren, bei dem alle Veränderungen des Zustands einer Softwareanwendung als Sequenz von Events abgebildet und aufgezeichnet werden.[1] Event Sourcing wird zu den Analysemustern gezählt.
Beispiel
Bei einem Onlineshop können Bestellungen aufgegeben werden. Anstatt den aktuellen Zustand der Bestellung einfach auf einer Datenbank-Tabelle zu persistieren, wird ein Event Bestellung erstellt persistiert. Soll ein Posten zur Bestellung hinzugefügt werden, wird ein Event Posten hinzugefügt persistiert. Weitere Events wären beispielsweise Bestellung bestätigt und Bestellung abgeschlossen. Möchte man nun den aktuellen Zustand der Bestellung wissen, so müssen alle Events, in der Reihenfolge in der sie erstellt wurden, wieder geladen werden. Auf die gleiche Weise kann auch ein beliebig anderer, früherer Zustand wiederhergestellt werden. Events werden nie gelöscht. Soll ein Posten entfernt werden, muss entsprechend ein Posten-entfernt-Event persistiert werden.
Event Store
Die Persistierung von Events erfolgt in einem sogenannten Event Store. Ein Event Store kann eine relationale Datenbank sein. Es gibt auch spezialisierte Event Stores. Grundsätzlich können Events aber auch einfach in einer Datei oder einer NoSQL-Datenbank etc. gespeichert werden. Ein Event Store könnte wie folgt mit einer relationalen Datenbank implementiert werden. Es ist zu beachten, dass dies lediglich ein Beispiel ist und viele mögliche Varianten denkbar sind:
| Globale Sequenznummer | Persistierungs-ID | Sequenznummer | Event | Event Typ |
|---|---|---|---|---|
| 1 | Bestellung|1 | 1 | <blob> | Bestellung erstellt |
| 2 | Bestellung|1 | 2 | <blob> | Posten hinzugefügt |
| 3 | Bestellung|2 | 1 | <blob> | Bestellung erstellt |
| 4 | Bestellung|1 | 3 | <blob> | Posten entfernt |
Zunächst gibt es eine globale (und eindeutige) Sequenznummer, welche aufsteigend vergeben wird. Dies erlaubt es Events dann auch wieder in der Reihenfolge zu lesen, wie sie erstellt wurden. Die Persistierungs-ID definiert, auf welche Persistierungseinheit sich das Event bezieht. Die Sequenznummer ist eine Fortlaufnummer, welche innerhalb der gleichen Persistierungseinheit eindeutig ist. Sie erlaubt es, Events zu einer Perisiterungseinheit in der richtigen Reihenfolge zu lesen. In der Spalte Event wird das eigentliche Event gespeichert. Dies kann im Prinzip in einem beliebigen Format erfolgen (z. B. JSON). Der Event Typ schlussendlich erlaubt die Deserialisierung der Event Daten und auch das Suchen nach bestimmten Events.
Ein Event Store bietet ggf. auch einen optimistischen Locking-Mechanismus an, damit Regeln strikte eingehalten werden können und nicht durch paralleles Hinzufügen von Events zu einer Persistierungseinheit ungültige Zustände erzeugt werden. Dies wird z. B. dadurch erreicht, dass der Schreibende dem Event Store mitteilt, welches die Sequenznummer des letzten Events ist, welches er erwartet. Sollte in der Zwischenzeit ein weiteres Event dazu gekommen sein, wird das Schreiben des Events abgelehnt.
Snapshots
Wenn viele Events geladen werden müssen, kann dies lange dauern. In solchen Fällen können sogenannte Snapshots erstellt werden. D.h. es wird z. B. nach zehn Events zusätzlich der aktuelle Zustand gespeichert. Beim Laden müssen dann nur noch die Events nach diesem Snapshot und nicht alle Events geladen werden.
Vorteile gegenüber CRUD
- Nachvollziehbarkeit der aufgezeichneten Änderungen
- Nachträgliche Datenanalyse der aufgezeichneten Änderungen
- Deterministische Fehleranalyse innerhalb der aufgezeichneten Änderungen möglich
- Kann im Kontext von verteilten Applikationen andere Muster wie z. B. das Transactional Outbox Pattern überflüssig machen
Nachteile gegenüber CRUD
- Erfordert eine andere Denkweise als z. B. CRUD-Applikationen
- Benötigt mehr Speicherplatz im Vergleich zu Systemen, die lediglich den aktuellen Zustand speichern
- Kann zu geringerer Performanz führen, da beim Lesen und Mutieren von Daten potentiell mehrere Events geladen werden müssen, um den aktuellen Zustand herzustellen. Dem kann jedoch z. B. mit Snapshots (für das Mutieren) und CQRS (für das Lesen) entgegengewirkt werden. Einen anderer Ansatz liefern Dynamic Consistency Boundaries[2]. Dabei kann die Anzahl der zu lesenden Events im Kontext einer Mutation, abhängig vom Use Case, minimiert werden
- Benötigt eine Sonderbehandlung für aus datenschutzrechtlicher Sicht zu löschende Daten
- Die Weiterentwicklung des Systems kann herausfordernd sein, da beispielsweise alte Events weiterhin verarbeitet werden können müssen, auch wenn sie nicht mehr neu produziert werden
CQRS
Eine Kombination von Event Sourcing und CQRS kann sinnvoll sein, ist aber nicht zwingend. Bei CQRS wird, basierend auf den Events, ein sogenanntes Lesemodell aufgebaut, welches für lesende Zugriffe verwendet wird. Dies erlaubt eine separate Skalierung von schreibenden und lesenden Zugriffen. Das Lesemodell kann z. B. den aktuellen Zustand direkt verfügbar machen, ohne dass alle Events geladen werden müssen.
Weblinks
- CQRS Journey. In: patterns & practices, MSDN. Microsoft, Juli 2012, abgerufen am 13. Juli 2014 (englisch, Tutorial zu CQRS und Event Sourcing in C#).
- Lev Gorodinski: Domain-Driven Design, Event Sourcing and CQRS with F# and EventStore. In: YouTube. DC F# Meetup, 6. März 2014, abgerufen am 13. Juli 2014 (englisch, CQRS und EventSourcing in F#).
- Introduction to Event Sourcing. In: EventSourcingDB documentation. the native web GmbH, 3. Mai 2025, abgerufen am 3. Mai 2025 (englisch, Einführung und Überblick zu Event-Sourcing).
Quellen
- ↑ Greg Young: CQRS Documents by Greg Young. Abgerufen am 16. Februar 2025 (englisch).
- ↑ Sara Pellegrini: I am here to kill the aggregate. 11. April 2023, abgerufen am 28. März 2025 (englisch).