Domain Driven Design und Event-Driven Architecture sind wie zwei Puzzleteile, die perfekt ineinander passen. Während DDD uns lehrt, Software um die Geschäftsdomäne herum zu strukturieren und die Fachsprache des Unternehmens in den Code zu überführen, zeigt uns EDA, wie wir diese fachlichen Konzepte durch Events natürlich miteinander verbinden können. Diese Synergie ist kein Zufall, sondern entspringt einer gemeinsamen Grundphilosophie: beiden Ansätzen geht es darum, die Komplexität von Geschäftsprozessen beherrschbar zu machen.
Denken Sie an ein großes E-Commerce-Unternehmen wie Amazon oder Zalando. Diese Organisationen müssen gleichzeitig mit Kunden, Produktkatalogen, Bestellungen, Zahlungen, Lagerverwaltung, Lieferungen und Marketing umgehen. Jeder dieser Bereiche hat seine eigene Fachsprache, eigene Regeln und eigene Experten.
Verschiedene Sichten auf den Begriff “Kunde”:
| Bereich | Sicht auf “Kunde” | Wichtige Eigenschaften |
|---|---|---|
| Vertrieb | Verkaufspotential & Präferenzen | Lead-Status, Kaufhistorie, Interessen |
| Buchhaltung | Zahlungsverhalten | Kreditwürdigkeit, Zahlungshistorie, Mahnverfahren |
| Support | Zufriedenheit & Probleme | Ticket-Historie, Beschwerden, Lösungszeiten |
| Marketing | Zielgruppensegment | Demografische Daten, Kampagnen-Response |
Ein Mitarbeiter aus der Logistik spricht anders über “Pakete” als ein Mitarbeiter aus dem Marketing über “Kampagnen”. Domain Driven Design hilft uns dabei, diese verschiedenen fachlichen Bereiche als separate Bounded Contexts zu modellieren, während Event-Driven Architecture uns zeigt, wie diese Contexts elegant miteinander kommunizieren können, ohne ihre Autonomie zu verlieren.
Was macht diese Kombination so mächtig? DDD gibt uns die Struktur und die Sprache, während EDA uns die Verbindungen liefert. Ein Bounded Context für “Order Management” kann Events wie “BestellungAufgegeben” publizieren, die von anderen Contexts wie “Inventory Management” oder “Customer Service” konsumiert werden. Dabei bleibt jeder Context in seiner eigenen fachlichen Welt, verwendet seine eigene Sprache und seine eigenen Modelle, aber sie können trotzdem nahtlos zusammenarbeiten.
Diese Integration ist besonders wertvoll, weil sie uns hilft, die richtigen Grenzen zu ziehen und die passenden Events zu identifizieren. Ohne DDD tendieren Teams dazu, Events entweder zu technisch zu modellieren (“DatabaseRowUpdated”) oder zu grob zu schneiden (“SomethingHappened”). Mit DDD als Leitfaden entstehen Events, die echte Geschäftsereignisse repräsentieren und für die Fachbereiche verständlich und wertvoll sind.
Um zu verstehen, wie Bounded Contexts und Event-Streams zusammenwirken, beginnen wir mit einer fundamentalen Erkenntnis aus Domain Driven Design: Verschiedene Teile einer Organisation haben unterschiedliche Sichten auf dieselben Konzepte. Das Wort “Kunde” bedeutet für den Vertrieb etwas anderes als für die Buchhaltung oder den Kundensupport. Der Vertrieb interessiert sich für Verkaufspotential und Präferenzen, die Buchhaltung für Zahlungshistorie und Kreditwürdigkeit, der Support für bisherige Probleme und Zufriedenheit.
Diese unterschiedlichen Sichtweisen führen in traditionellen Systemen oft zu dem Versuch, ein einheitliches “Kunden”-Modell zu schaffen, das alle Aspekte abdeckt. Das Ergebnis ist meist ein komplexes, schwer verständliches Datenmodell, das niemanden richtig zufriedenstellt. Domain Driven Design schlägt einen anderen Weg vor: Akzeptieren Sie, dass verschiedene Contexts verschiedene Modelle brauchen, und ziehen Sie klare Grenzen zwischen diesen Contexts.
Event-Driven Architecture macht diese Trennung nicht nur möglich, sondern natürlich und elegant. Jeder Bounded Context kann seine eigenen Events definieren und publizieren, die seine spezifische Sicht auf die Geschäftswelt widerspiegeln.
Event-Stream als Kommunikationskanal zwischen Contexts:
Der “Sales Context” könnte ein “ProspectContactedEvent” publizieren, wenn ein Vertriebsmitarbeiter einen potentiellen Kunden kontaktiert. Dieses Event enthält die Information, die für den Vertrieb relevant ist:
Der “Customer Service Context” muss nicht alle Details dieses Events verstehen oder verarbeiten. Er könnte lediglich daran interessiert sein, dass ein Kundenkontakt stattgefunden hat, um seine eigene Sicht auf die Kundenbeziehung zu aktualisieren. Dabei transformiert er das eingehende Event in sein eigenes internes Modell und seine eigene Sprache. Diese Transformation ist kein Zeichen schlechten Designs, sondern ein bewusster Akt der Kontextabgrenzung.
Event-Streams werden zu den Kommunikationskanälen zwischen Bounded Contexts. Angenommen jeder Context hätte seinen eigenen Radiosender, auf dem er regelmäßig sendet, was in seiner Welt passiert. Andere Contexts können diese Sender empfangen und entscheiden, welche Nachrichten für sie relevant sind. Wichtig ist dabei, dass der Sender nicht wissen muss, wer zuhört.
| Context | Published Events | Consumed Events |
|---|---|---|
| Order Management | OrderPlaced, OrderCancelled | CustomerVerified, PaymentProcessed |
| Payment | PaymentProcessed, PaymentFailed | OrderPlaced |
| Inventory | StockReserved, StockDepleted | OrderPlaced, OrderCancelled |
| Shipping | OrderShipped, DeliveryCompleted | OrderPlaced, PaymentProcessed |
Der “Order Management Context” publiziert “BestellungAufgegeben”-Events, ohne sich darum zu kümmern, ob und wie viele andere Contexts diese Events konsumieren.
Diese lose Kopplung zwischen Contexts ist entscheidend für die Skalierbarkeit und Wartbarkeit großer Systeme. Neue Contexts können hinzugefügt werden, die beginnen, bestehende Event-Streams zu konsumieren, ohne dass die produzierenden Contexts geändert werden müssen. Ein neuer “Analytics Context” könnte alle Bestell-Events konsumieren, um Verkaufstrends zu analysieren, ohne dass der “Order Management Context” davon weiß oder dadurch beeinflusst wird.
Die Herausforderung liegt darin, die richtigen Context-Grenzen zu identifizieren. Ein zu großer Context wird unüberschaubar und verliert die Vorteile der fachlichen Abgrenzung. Ein zu kleiner Context führt zu übermäßiger Fragmentierung und komplexer Orchestrierung. Die Erfahrung zeigt, dass Context-Grenzen oft entlang organisatorischer Grenzen verlaufen. Verschiedene Teams, verschiedene Fachbereiche oder verschiedene Verantwortlichkeiten deuten auf separate Contexts hin.
Praktisches Beispiel - Lagerbestand-Event:
Ein praktisches Beispiel verdeutlicht diese Konzepte: In unserem E-Commerce-System könnte der “Inventory Context” ein “LagerbestandNiedrig”-Event publizieren, wenn ein Artikel unter eine kritische Schwelle fällt. Verschiedene Contexts reagieren darauf in ihrer eigenen Weise:
Jeder Context reagiert mit seinen eigenen Geschäftsregeln, aber alle profitieren von der gleichen grundlegenden Information.
Aggregate sind eines der mächtigsten Konzepte aus Domain Driven Design, und ihre Beziehung zu Events ist fundamental für das Verständnis event-getriebener Systeme. Ein Aggregate ist eine Gruppe zusammengehöriger Objekte, die als Einheit behandelt werden müssen, um die Geschäftsinvarianten aufrechtzuerhalten. Wenn wir Events in diese Gleichung einbringen, entstehen Muster, die sowohl elegant als auch leistungsfähig sind.
Betrachten wir zunächst, was ein Aggregate charakterisiert. Es hat eine klar definierte Grenze, einen eindeutigen Identifier (die Aggregate Root) und kapselt Geschäftslogik, die sicherstellt, dass das Aggregate immer in einem konsistenten Zustand bleibt. In unserem E-Commerce-System könnte eine “Bestellung” ein Aggregate sein, das aus Bestellpositionen, Lieferadresse, Zahlungsinformationen und Status besteht.
Bestellungs-Aggregate Struktur:
Order Aggregate Root
├── Order ID (Identifier)
├── Customer Information
├── Order Items []
│ ├── Product ID
│ ├── Quantity
│ └── Price
├── Shipping Address
├── Payment Information
└── Order Status
Das Aggregate stellt sicher, dass beispielsweise niemals Artikel zu einer bereits abgeschlossenen Bestellung hinzugefügt werden können.
Events entstehen natürlich aus den Geschäftsoperationen, die auf Aggregates ausgeführt werden. Wenn ein neues Bestellungs-Aggregate erstellt wird, führt dies zu einem “BestellungErstellt”-Event. Wenn sich der Status ändert, entsteht ein “BestellungsStatusGeändert”-Event. Diese Events sind nicht willkürlich, sondern spiegeln die bedeutsamen Geschäftsereignisse wider, die aus der Sicht des Aggregates aufgetreten sind.
Event-Entstehung aus Geschäftsoperationen:
| Geschäftsoperation | Ausgelöstes Event | Fachliche Bedeutung |
|---|---|---|
| Neue Bestellung erstellen | OrderCreated | Kunde hat Kaufentscheidung getroffen |
| Artikel hinzufügen | ItemAddedToOrder | Warenkorb wurde erweitert |
| Zahlung verarbeiten | PaymentProcessed | Finanzielle Transaktion abgeschlossen |
| Bestellung versenden | OrderShipped | Physische Erfüllung gestartet |
| Stornierung | OrderCancelled | Geschäftsprozess wurde abgebrochen |
Die Aggregate Root fungiert als Event-Publisher für ihr gesamtes Aggregate. Sie sammelt Events während der Ausführung von Geschäftsoperationen und publiziert sie, nachdem die Änderungen erfolgreich persistiert wurden. Diese Reihenfolge ist wichtig: erst die Zustandsänderung, dann das Event. Dadurch wird sichergestellt, dass Events nur publiziert werden, wenn die zugrundeliegende Geschäftsoperation tatsächlich erfolgreich war.
Ein wichtiges Prinzip ist, dass Events die Geschäftsoperationen widerspiegeln sollten, nicht die technischen Details der Implementierung. Ein “BestellungErstellt”-Event sollte die fachlich relevanten Informationen über die Bestellung enthalten, nicht die Tatsache, dass drei Datenbankzeilen eingefügt wurden. Diese fachliche Ausrichtung macht Events wertvoll für andere Contexts und für Geschäftsanalysen.
Konsistenzgrenzen und Event-Verarbeitung:
Die Granularität von Events sollte sich an der Granularität von Geschäftsoperationen orientieren. Eine komplexe Geschäftsoperation wie “Bestellung aufgeben” könnte mehrere interne Schritte umfassen: Verfügbarkeit prüfen, Preise berechnen, Rabatte anwenden, Lieferkosten ermitteln. Aber für die Außenwelt ist das ein einziger fachlicher Vorgang, der zu einem “BestellungAufgegeben”-Event führt, nicht zu einer Reihe von technischen Events.
Interne Schritte einer Bestellung:
1. Verfügbarkeit prüfen ─┐
2. Preise berechnen ├─→ Ein "OrderPlaced" Event
3. Rabatte anwenden │
4. Lieferkosten ermitteln ┘
Nicht: CheckAvailability, CalculatePrice, ApplyDiscount, ... Events
Aggregate-Event-Beziehungen helfen auch dabei, Konsistenzgrenzen zu definieren. Starke Konsistenz wird innerhalb eines Aggregates gewährleistet, während zwischen Aggregates eventual consistency über Events akzeptiert wird.
Konsistenzmodell:
| Bereich | Konsistenztyp | Mechanismus |
|---|---|---|
| Innerhalb Aggregate | Stark (ACID) | Datenbanktranskation |
| Zwischen Aggregates | Eventually | Event-basierte Synchronisation |
| Zwischen Contexts | Eventually | Asynchrone Event-Streams |
Wenn eine Bestellung aufgegeben wird, muss innerhalb des Bestellungs-Aggregates sofort sichergestellt werden, dass alle Bestellpositionen gültig sind und die Gesamtrechnung stimmt. Die Reduzierung des Lagerbestands in einem anderen Aggregate kann jedoch asynchron über Events erfolgen.
Event Sourcing ist eine besonders interessante Erweiterung dieser Konzepte. Anstatt nur Events zu publizieren, können Aggregates vollständig durch die Sequenz der Events rekonstruiert werden, die zu ihrem aktuellen Zustand geführt haben. Das Bestellungs-Aggregate würde dann aus Events wie “BestellungErstellt”, “ArtikelHinzugefügt”, “RabattAngewendet”, “BestellungBestätigt” aufgebaut.
Diese Herangehensweise bietet eine vollständige Audit-Spur und ermöglicht es, den Zustand zu jedem beliebigen Zeitpunkt zu rekonstruieren.
Die Ubiquitous Language ist eines der wertvollsten Konzepte aus Domain Driven Design, und ihre konsequente Anwendung auf Events kann den Unterschied zwischen einem System ausmachen, das die Geschäftswelt klar widerspiegelt, und einem, das nur Entwickler verstehen. Ubiquitous Language bedeutet, dass Fachexperten, Entwickler, Produktmanager und alle anderen Beteiligten dieselbe Sprache verwenden, um über die Geschäftsdomäne zu sprechen.
Gute vs. schlechte Event-Namen:
| ❌ Technische Namen | ✅ Geschäftssprachliche Namen | Verbesserung |
|---|---|---|
| DatabaseRowInserted | OrderPlaced | Geschäftsereignis statt technisches Detail |
| EntityStateChanged | CustomerUpgraded | Spezifische Bedeutung statt allgemein |
| DataUpdated | PriceAdjusted | Klare Intention der Änderung |
| MessageProcessed | PaymentVerified | Fachlicher Prozessschritt |
| RecordModified | ShippingAddressUpdated | Konkrete Geschäftsaktion |
Wenn wir Events benennen und strukturieren, haben wir eine außergewöhnliche Gelegenheit, diese gemeinsame Sprache zu stärken und zu verfeinern. Events repräsentieren die bedeutsamsten Momente im Leben einer Geschäftsdomäne. Sie sind die Geschichten, die das System erzählt: “Ein Kunde hat eine Bestellung aufgegeben”, “Eine Zahlung wurde verarbeitet”, “Ein Artikel wurde versandt”. Diese Geschichten sollten in einer Sprache erzählt werden, die sowohl für Geschäftsexperten als auch für Entwickler verständlich und meaningful ist.
Die Namensgebung von Events ist dabei der erste und wichtigste Schritt. Ein Event namens “OrderPlaced” spricht sowohl Entwickler als auch Geschäftsleute an. Es ist klar, präzise und verwendet die Terminologie, die im Unternehmen üblich ist. Im Gegensatz dazu wäre ein Event namens “DatabaseRowInserted” oder “EntityStateChanged” nur für Entwickler verständlich und würde die Gelegenheit versäumen, die Geschäftssprache zu stärken.
Event-Namenskonventionen:
Aber es geht über die reine Namensgebung hinaus. Die Struktur und der Inhalt von Events sollten die fachlichen Konzepte und Beziehungen widerspiegeln. Ein “BestellungStorniert”-Event sollte nicht nur eine ID enthalten, sondern alle Informationen, die für das Verständnis dieses Geschäftsereignisses relevant sind:
{
"eventType": "OrderCancelled",
"orderId": "ORD-2025-001234",
"customerId": "CUST-98765",
"cancellationReason": "Customer requested",
"cancelledBy": "customer",
"refundAmount": 149.99,
"affectedItems": ["SKU-001", "SKU-002"],
"cancellationTimestamp": "2025-01-15T14:30:00Z"
}Die Entwicklung einer Ubiquitous Language ist ein iterativer Prozess, der Events als Katalysator nutzen kann. Wenn Teams beginnen, Events zu modellieren, entstehen natürlich Diskussionen über die präzise Bedeutung von Geschäftsbegriffen.
Klärende Fragen bei Event-Modellierung:
Diese Diskussionen führen zu einem schärferen Verständnis der Geschäftsprozesse und zu präziseren Definitionen.
Context-spezifische Event-Sprachen:
Die Konsistenz der Event-Sprache über verschiedene Bounded Contexts hinweg ist eine besondere Herausforderung. Verschiedene Contexts können unterschiedliche Terminologie für ähnliche Konzepte verwenden, und das ist oft richtig und notwendig.
| Context | Terminologie | Event-Beispiele |
|---|---|---|
| Sales | Leads, Opportunities, Prospects | ProspectContacted, LeadConverted |
| Fulfillment | Orders, Shipments, Deliveries | OrderFulfilled, PackageDispatched |
| Customer Service | Tickets, Cases, Issues | SupportTicketOpened, IssueResolved |
| Marketing | Campaigns, Segments, Conversions | CampaignLaunched, SegmentUpdated |
Integration Events, die zwischen Contexts fließen, bieten eine Gelegenheit für bewusste Übersetzung zwischen verschiedenen Ubiquitous Languages:
Sales Context: Marketing Context:
ProspectConverted → NewCustomerRegistered
Internal Events: Integration Events:
- ProspectStatusChanged - CustomerRegistrationCompleted
- SalesOpportunityClosed - CustomerSegmentAssigned
Event-Workshop Agenda:
Event-Workshops mit Fachexperten sind ein mächtiges Werkzeug zur Entwicklung der Ubiquitous Language:
Die Dokumentation von Events sollte wie ein Geschäftslexikon fungieren. Jedes Event sollte nicht nur seine technische Struktur dokumentieren, sondern auch seine fachliche Bedeutung, die Umstände, unter denen es auftritt, und seine Auswirkungen auf andere Geschäftsprozesse.
Letztendlich führt eine konsequent angewendete Ubiquitous Language für Events zu Systemen, die nicht nur technisch robust sind, sondern auch als natürliche Erweiterung der Geschäftsprozesse empfunden werden. Events werden zu einer Sprache, in der sowohl Menschen als auch Maschinen über das Geschäft kommunizieren können, und schaffen so eine Brücke zwischen der technischen und der fachlichen Welt.