DDD & Co., Teil 5: Event Sourcing

Wer eine Anwendung mit DDD modelliert, braucht für die Implementierung einen Ansatz zum Umsetzen der Persistenz. Dafür lässt sich eine herkömmliche CRUD-Datenbank verwenden, doch es gibt einen besseren Ansatz: Event Sourcing. Wie funktioniert das?

In Pocket speichern vorlesen Druckansicht
Lesezeit: 6 Min.
Von
  • Golo Roden
Inhaltsverzeichnis

Wer eine Anwendung mit DDD modelliert, braucht für die Implementierung einen Ansatz zum Umsetzen der Persistenz. Dafür lässt sich eine herkömmliche CRUD-Datenbank verwenden, doch es gibt einen besseren Ansatz: Event Sourcing. Wie funktioniert das?

Die Daten, die in einer mit DDD modellierten Anwendung entstehen, sind fachliche Ereignisse. Jedes Ereignis beschreibt eine Änderung des Zustands, sodass der aktuelle Zustand der Summe der Auswirkungen aller vergangenen Ereignisse entspricht. Dieser Zustand lässt sich speichern.

Mehr Infos

Das Vorgehen funktioniert, doch man vergibt ein großes Potenzial: Wird nur der aktuelle Zustand gespeichert, weiß man nicht, wie er entstanden ist. Wer die Vergangenheit aber im Detail kennt, kann sie auswerten und Schlüsse für die Zukunft ziehen – und den aktuellen Zustand en passant ermitteln, indem alle Ereignisse aus der Vergangenheit nochmals abgespielt und ihre Änderungen nachvollzogen werden.

Diesen Vorgang bezeichnet man als Replay. Auf die Weise kann aber mehr als lediglich der aktuelle Zustand ermittelt werden. Wertet man nur einen Teil der fachlichen Ereignisse aus, lässt sich auch der Zustand von vor vier Wochen herausfinden. Oder der von vor einem Jahr. Oder der zu jedem beliebigen anderen Zeitpunkt in der Vergangenheit. Das ermöglicht das Vergleichen von Daten im Verlauf der Zeit.

Gleichzeitig ermöglicht das Vorgehen auch, Fragen im Nachhinein zu beantworten, von denen man zunächst gar nicht wusste, dass sie interessant sein könnten. Wer bei jeder Änderung stets nur ein UPDATE ausführt, hat keine Informationen über den historischen Verlauf.

Stehen statt dessen die fachlichen Ereignisse aus der Vergangenheit zur Verfügung, kann man sich deren Semantik zu Nutze machen. Auf das Beispiel der To-do-Liste bezogen, bedeutet das, dass sich Fragen beantworten lassen wie:

  • Wie viel Zeit vergeht zwischen dem Notieren und dem Erledigen einer Aufgabe?
  • Wie häufig wird eine Aufgabe editiert, bevor sie als erledigt markiert wird?
  • Wie viele Aufgaben werden verworfen, nachdem sie mindestens dreimal editiert wurden?
  • Wie häufig wird innerhalb von 30 Sekunden das Abhaken einer Aufgabe rückgängig gemacht?

Alle diese Fragen lassen sich beantworten, wenn man die über die Historie der fachlichen Ereignisse noted, edited, tickedOff, resumed, discarded und archived verfügt.

Statt bei Änderungen also ein UPDATE oder gar ein DELETE auszuführen, speichert man die fachlichen Ereignisse als Deltas in einer stets wachsenden Liste. Das Vorgehen wird als Event Sourcing bezeichnet. Technisch ist es sehr einfach zu implementieren, da man lediglich die beiden SQL-Anweisungen INSERT und SELECT benötigt. Eine Datenbank, die auf die Weise arbeitet, nennt man Event Store.

Weder das Konzept des Event Sourcing noch das Konstrukt des Event Stores sind Bestandteile von DDD. Sie lassen sich auch ohne DDD verwenden, genauso wie man DDD ohne Event Sourcing beziehungsweise einen Event Store nutzen kann. Trotzdem passen die Bausteine gut zueinander, weshalb es sich anbietet, beides zu kombinieren.

Der Ansatz ist übrigens nicht neu: Jede Bank verwaltet die bei ihr angelegten Konten auf genau die Art. Einzahlung und Abhebungen werden als fachliche Ereignisse gespeichert, der aktuelle Kontostand ergibt sich aus dem Replay aller jemals getätigten Einzahlung und Abhebungen. Ein Sparbuch ist daher ein perfektes Beispiel aus der realen Welt für praktisches Event Sourcing.

Man mag einwenden, dass der Ansatz, eine stetig wachsende Liste von fachlichen Ereignissen zu pflegen zwar theoretisch durchaus interessant, im Alltag aber höchst unpraktisch sei. Immerhin steigt der Speicherbedarf zunehmend an, und das Replay wird mit jedem Ereignis langsamer. Beide Einwände sind für sich genommen zwar korrekt, tatsächlich aber irrelevant.

Schaut man sich den Vorgang des Replays nämlich genauer an, fällt auf, dass zu einem großen Teil stets die gleichen fachlichen Ereignisse abgespielt werden. Da die Liste nur am Ende um neue Ereignisse ergänzt wird, bleibt ihr Anfang stets gleich. Weil es in einem Event Store zudem weder die Operation UPDATE noch DELETE gibt, lässt sich das sogar garantieren.

Das bedeutet, dass das Ergebnis eines Replays als Snapshot gespeichert und später als Ausgangsbasis für einen weiteren Replay wiederverwendet werden kann. So lassen sich Replays praktisch beliebig beschleunigen, je nachdem, wie häufig ein Snapshot erzeugt wird.

Snapshots ermöglichen außerdem auch, dass alle fachlichen Ereignisse, die vor einem Snapshot liegen, tatsächlich gelöscht werden können. Damit verliert man zwar die Möglichkeit zur individuellen Analyse, aber der Datenbestand lässt sich von Zeit zu Zeit bereinigen, beispielsweise um Speicherplatz zu sparen.

Was bedeutet das nun konkret, wenn man DDD und Event Sourcing verbinden möchte? Soll eine Anwendung Kommandos verarbeiten, benötigt man dazu ein Aggregat, das die zugehörige Geschäftslogik enthält. Als Ergebnis entstehen fachliche Ereignisse, die den Zustand des Aggregats verändern. Diese Ereignisse gilt es, in einem Event Store zu speichern.

Wenn ein Aggregat geladen wird, muss man seine fachlichen Ereignisse aus dem Event Store laden und abspielen, um den aktuellen Zustand des Aggregats wiederherzustellen. Damit der Vorgang im Lauf der Zeit nicht immer länger dauert, erzeugt der Event Store regelmäßig Snapshots, um den Replay zu beschleunigen.

Da der Großteil der Aufgaben generisch ist, kann ein Anwendungsentwickler den Fokus auf das Entwickeln der Aggregate, der Kommandos und fachlichen Ereignisse legen. Diese Aspekte sind tatsächlich domänenspezifisch, alles andere lässt sich abstrahieren. Wie das funktioniert, wird der nächste Teil der Artikelreihe zeigen.

tl;dr: Event Sourcing ist ein Ansatz, Änderungen als Deltas und nicht deren Auswirkungen zu speichern. Die zugehörige Datenbank wird als Event Store bezeichnet und verwendet ausschließlich die SQL-Anweisungen INSERT und SELECT. Obwohl Event Sourcing kein Bestandteil von DDD ist, ergänzen sich beide Konzepte ausgesprochen gut. ()