Event Sourcing für SaaS: Wann sich die Komplexität lohnt und wann nicht

#Event Sourcing
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Event Sourcing gehört zu den Mustern, die klingen, als würden sie alles lösen. Speichere jede Änderung an deinem System als unveränderliche Folge von Events, und du bekommst einen perfekten Audit-Trail, die Möglichkeit, jeden vergangenen Zustand zu rekonstruieren, und eine saubere Trennung zwischen dem, was passiert ist, und der Art, wie du es liest. Für ein SaaS-Team, das unter Druck steht, Compliance nachzuweisen und Reporting-Features zu liefern, ist dieser Pitch verführerisch.

In der Praxis löst Event Sourcing eine spezifische Klasse von Problemen zu erheblichen Komplexitätskosten. Die Einführung verändert, wie dein Team Daten modelliert, wie ihr deployt, wie ihr debuggt und wie ihr neue Entwickler einarbeitet. Manchmal ist dieser Tausch klar lohnend. Häufiger greift ein Team zu Event Sourcing, obwohl eine schlichte Audit-Log-Tabelle denselben Job zu einem Bruchteil des Aufwands erledigt hätte. Dieser Beitrag ist die ehrliche Bewertung: die Fälle, in denen sich das Muster bezahlt macht, die Fälle, in denen nicht, und ein konkreter Symfony-Vergleich, damit du den Unterschied im Codeumfang siehst.

Was Event Sourcing tatsächlich verändert

In einem konventionellen System speicherst du den aktuellen Zustand. Eine subscription-Zeile hat eine status-Spalte, und wenn ein Kunde kündigt, überschreibst du active mit cancelled. Der vorherige Wert ist weg, sofern du ihn nicht irgendwo protokolliert hast.

In einem Event-sourced System speicherst du den aktuellen Zustand nie als Quelle der Wahrheit. Du speicherst einen Strom von Events: SubscriptionCreated, PlanUpgraded, PaymentFailed, SubscriptionCancelled. Der aktuelle Zustand ist eine Projektion, die du durch das Abspielen dieser Events berechnest. Das Event-Log ist append-only und unveränderlich, und genau diese Unveränderlichkeit ist der Kern der Sache. Nichts wird je überschrieben, also kannst du immer beantworten, was passiert ist und in welcher Reihenfolge.

Diese eine Designentscheidung hat Konsequenzen, die sich durch die gesamte Codebasis ziehen. Lesezugriffe kommen nicht mehr aus derselben Quelle wie Schreibzugriffe, weshalb Event Sourcing typischerweise mit CQRS (Command Query Responsibility Segregation) kombiniert wird, also mit separaten, fürs Abfragen optimierten Read Models. Schemaänderungen werden zu Event-Versionierungsproblemen. Debugging bedeutet, über eine Folge von Events nachzudenken, statt eine Zeile zu inspizieren. Nichts davon ist unlösbar, aber all das ist Arbeit, die ein CRUD-System schlicht nicht hat.

Die vier Fälle, in denen Event Sourcing die richtige Wahl ist

Event Sourcing rechtfertigt seine Komplexität, wenn das Event-Log kein Overhead ist, sondern ein Kernbestandteil des Produkts. Es gibt vier Situationen, in denen das wirklich zutrifft.

Die erste: Audit-taugliche Nachvollziehbarkeit ist eine Produktanforderung und nicht nur eine Compliance-Checkbox. Es gibt einen echten Unterschied zwischen einem Kunden, der eine Änderungshistorie sehen möchte, und einem Regulierer, der ein manipulationssicheres, vollständiges, geordnetes Protokoll jedes Zustandsübergangs verlangt, etwa in Fintech-, Gesundheits- oder Legal-Software. Wenn das unveränderliche Log etwas ist, das du verkaufst, liefert Event Sourcing es per Konstruktion statt als nachträglich angeschraubtes Extra.

Die zweite: Das Abspielen der Historie zur Berechnung neuer Read Models ist ein Kernfeature. Wenn dein Produkt regelmäßig neue analytische Fragen über vergangenes Verhalten beantworten muss und du diese Antworten aus der rohen Historie ableiten willst statt aus den Aggregaten, die du damals zufällig gespeichert hast, glänzt Event Sourcing. Du kannst nächstes Jahr eine völlig neue Projektion aus Events bauen, die du dieses Jahr aufgezeichnet hast, ohne die zugrunde liegenden Details verloren zu haben.

Die dritte: Komplexe zeitbezogene Abfragen sind häufig. Fragen wie "Wie sah dieses Konto letzten Dienstag um 15 Uhr aus?" oder "Wie hat sich dieser Datensatz über den Abrechnungszeitraum verändert?" sind in einem System, das nur den aktuellen Zustand speichert, mühsam. Event Sourcing macht die Rekonstruktion eines Zeitpunkts natürlich, weil das Wiederherstellen vergangener Zustände einfach das Abspielen der Events bis zu einem Zeitstempel ist.

Die vierte: Undo und Redo sind ein zentrales Element der User Experience. Design-Tools, Dokumenteditoren und Workflow-Builder, in denen Nutzer erwarten, durch ihre eigene Historie vor und zurück zu springen, lassen sich fast direkt auf einen Event-Stream abbilden. Die Events, die du für die Domäne speicherst, sind dieselben Events, die den Undo-Stack antreiben.

Wenn eine oder mehrere dieser Situationen dein Produkt beschreiben, ist Event Sourcing kein Over-Engineering. Es ist das Muster, das zum Problem passt.

Die häufigeren Fälle, in denen es nur Komplexität hinzufügt

Die meisten SaaS-Anwendungen fallen nicht in diese vier Kategorien, und trotzdem zu Event Sourcing zu greifen heißt, für Fähigkeiten zu bezahlen, die du nie nutzen wirst. Drei Muster tauchen immer wieder auf.

Das häufigste ist Standard-CRUD-SaaS, das Audit-Logging braucht. Das Team hört "wir müssen nachverfolgen, wer was geändert hat" und schließt daraus, dass es Event Sourcing braucht. Braucht es nicht. Eine append-only Audit-Log-Tabelle, die parallel zu deinen normalen Updates geschrieben wird, erfasst Akteur, Zeitstempel, Entität sowie die Werte vorher und nachher. Sie beantwortet neunzig Prozent der echten Audit-Fragen zu etwa einem Prozent der Komplexität von vollem Event Sourcing, und dein primäres Datenmodell bleibt aus langweiligen, gut abfragbaren Zeilen.

Das zweite ist analytisches Reporting. Wenn das Ziel Dashboards, Trends und aggregierte Metriken sind, löst eine separate Analytics-Datenbank oder ein per Change Data Capture gespeistes Warehouse das Problem direkter, als Read Models aus einem Domain-Event-Stream neu aufzubauen. Reporting will denormalisierte, abfragefreundliche Daten, und dorthin kommst du, ohne deinen transaktionalen Kern zu event-sourcen.

Das dritte sind einfache zeitbezogene Abfragen. Wenn du gelegentlich einen früheren Wert oder einen stichtagsbezogenen Datensatz brauchst, erledigen bitemporale Tabellen oder sogar eine History-Tabelle mit Gültig-von- und Gültig-bis-Spalten das ohne das operative Gewicht eines Event Stores. Du bekommst Antworten zu beliebigen Zeitpunkten mit schlichtem SQL, das jeder Entwickler im Team bereits versteht.

Der rote Faden: Jedes dieser Bedürfnisse wird von einer gezielten, gut verstandenen Technik erfüllt. Event Sourcing bündelt sie alle und berechnet dir den vollen Preis, egal ob du das ganze Bündel brauchst oder nicht.

Ein konkreter Symfony-Vergleich

Der abstrakte Trade-off wird im Code klarer. Nimm ein Abonnement, das erstellt, hochgestuft und gekündigt werden kann, mit der Anforderung, eine Historie dieser Änderungen zu führen.

Der konventionelle Ansatz ist eine Doctrine-Entität plus Audit-Log. Die Subscription-Entität hält den aktuellen Zustand als gewöhnliche Properties, und ein kleiner Listener oder ein expliziter Aufruf schreibt bei jeder Änderung eine AuditLogEntry-Zeile mit Akteur, Feld, altem Wert, neuem Wert und Zeitstempel. Ein Entwickler, der neu ins Team kommt, liest die Entität und versteht die Domäne sofort. Abfragen sind schlichte Doctrine-Queries. Die Audit-Historie ist eine zweite Tabelle, aus der du per SELECT liest, wenn jemand fragt. Insgesamt sind das eine Entität, eine Audit-Entität und ein paar Zeilen in deinen Service-Methoden.

Der Event-sourced Ansatz ersetzt die Entität durch ein Aggregat. Subscription hat keine Setter mehr, die Zustand mutieren. Stattdessen erzeugen Commands Events, eine apply-Methode mutiert den Zustand im Speicher aus jedem Event, und die Events werden in einen Event Store persistiert. Das aktuelle Abonnement zu lesen bedeutet, seinen Stream zu laden und die Events wieder zu Zustand zu falten, oder ein separates Read Model zu pflegen, das von Projektionen aktuell gehalten wird. Du besitzt jetzt Event-Klassen, ein Aggregat, das sie aufzeichnet und anwendet, einen Event Store, Serialisierung und Versionierung für den Fall, dass sich Event-Formen ändern, und Projektionscode für jedes Read Model. Die Domänenlogik ist wohl sauberer, aber die umgebende Maschinerie hat ein Mehrfaches des Umfangs der konventionellen Variante, und jedes dieser beweglichen Teile ist etwas, das dein Team in Produktion wartet und debuggt.

Für ein Abonnement mit Historienanforderung ist die konventionelle Variante die richtige Wahl. Das Audit-Log liefert die Nachvollziehbarkeit, die tatsächlich gebraucht wurde, und der Code bleibt klein genug, dass eine neue Kollegin an einem Nachmittag produktiv ist. Die Event-sourced Variante beginnt erst zu gewinnen, wenn einer der vier echten Anwendungsfälle auftaucht. Dann hört ihre Maschinerie auf, Overhead zu sein, und wird zum Feature.

Wie du entscheidest

Der Test ist nicht, ob Event Sourcing elegant wäre. Sondern ob das unveränderliche, abspielbare Event-Log eine Fähigkeit ist, die dein Produkt braucht, oder nur etwas, das rigoros klingt. Schreib zuerst die konkrete Anforderung auf. Lautet sie "nachverfolgen, wer was geändert hat", willst du ein Audit-Log. Lautet sie "Kunden sollen ihre gesamte Bearbeitungshistorie abspielen und verzweigen können", willst du Event Sourcing. Die Anforderung sollte führen, nicht das Muster.

Sei auch ehrlich bei den Kosten. Event Sourcing hebt die Untergrenze der nötigen operativen Reife: Du brauchst Leute, die mit Eventual Consistency, Projektions-Rebuilds und Event-Versionierung vertraut sind, und zwar für die gesamte Lebensdauer des Systems, nicht nur für den ersten Sprint. Wenn dein Team klein und deine Domäne gewöhnlich ist, liegt diese Untergrenze hoch.

Das ist genau die Art von Architekturentscheidung, die früh billig richtig zu treffen und später teuer zu korrigieren ist, und genau dort zahlt sich ein Blick von außen aus. Wenn du für einen neuen SaaS-Build Event Sourcing gegen ein einfacheres Modell abwägst, ist unsere Arbeit in Custom Software Development und Tech-Stack-Strategie genau um diese Trade-offs herum gebaut. Wenn du bereits ein Muster eingeführt hast, das jetzt gegen dich arbeitet, kann ein Code-Quality-Consulting einen Weg zurück zu etwas Wartbarem aufzeigen.

Event Sourcing ist ein mächtiges Muster, das für eine schmale Klasse von Problemen wirklich richtig und für viele andere wirklich falsch ist. Die Disziplin liegt darin, zu erkennen, welches Problem du hast, bevor du dich festlegst. Wenn du eine zweite Meinung zu dieser Entscheidung willst, schreib an hello@wolf-tech.io oder besuch wolf-tech.io. Wir helfen dir lieber, die Komplexität zu vermeiden, als sie dir später gegen Rechnung wieder auszubauen.