Dein User-Model hat 47 Felder. Ich weiß das, weil jedes User-Model irgendwann 47 Felder hat.
Marketing hat segmentId hinzugefügt. Billing braucht stripeCustomerId. Support will lastTicketDate. HR hat employeeLevel drangehängt für interne Nutzer. Jedes Feature-Team tackert seine Felder an das Model, weil “User” das zentrale Ding ist. Das Gravitationszentrum.
Bis es kollabiert. Und das passiert nicht nur mit User, sondern mit Order, Product, Customer. Mit jedem Konzept das mehr als ein Team anfasst.
Klick dich durch und schau, wie schnell es kippt:
Fünf Schritte. Von vier Feldern zu neunzehn. Und dann ändert Billing ein Feld und drei andere Teams brechen. Das ist kein hypothetisches Szenario. Das ist Dienstag.
Irgendwann sagt jemand: “Wir brauchen DDD.” Und dann passiert das Falsche. Jemand liest die ersten drei Kapitel von Evans’ Blue Book, benennt die Ordner um (entities/, repositories/, valueObjects/, services/), und das Team erklärt DDD für implementiert.
Das war nicht DDD. Das war ein Refactoring der Ordnerstruktur.
Was DDD wirklich löst
Domain-Driven Design hat zwei Hälften. Die taktische (Entities, Value Objects, Aggregates, Repositories) ist der Teil den alle kennen. Die strategische (Bounded Contexts, Ubiquitous Language, Context Mapping) ist der Teil der tatsächlich Probleme löst.
Rate mal, welche Hälfte die meisten Teams ignorieren.
Die strategische Seite beantwortet eine einzige Frage: wem gehört dieses Konzept? Nicht Code Ownership. Sondern: wer definiert, was “Kunde” bedeutet?
Ubiquitous Language
Der wichtigste Begriff in DDD ist nicht “Entity” oder “Aggregate.” Es ist Ubiquitous Language: die Idee, dass jeder Bounded Context seine eigene Sprache hat. Und dass dasselbe Wort in verschiedenen Kontexten verschiedene Dinge bedeutet.
Klick dich durch die Kontexte und schau was “Kunde” in jedem bedeutet:
Sales braucht Lead Score und Budget. Billing braucht Zahlungsbedingungen und Steuernummer. Support braucht Ticket-Historie. Shipping braucht Lieferadressen. Niemand braucht alles.
Das klingt nach Duplikation. Ist es auch. Aber Duplikation zwischen Kontexten ist gewollt, billiger als die Kopplung die du bekommst wenn alle dasselbe Model teilen. Das ist der DDD-Trade-off den die meisten nicht akzeptieren wollen.
Wenn du das in ein Customer-Interface packst, bekommst du 20 Felder, von denen jeder Kontext 5 braucht und 15 ignoriert. Genau das Problem das du oben mit dem Order-Model gesehen hast.
Der Ausweg: jeder Kontext definiert sein eigenes Model. SalesLead, BillingAccount, SupportContact, ShippingRecipient. Vier Interfaces statt einem. Keine Kopplung.
Bounded Contexts finden
Bounded Contexts folgen meistens Teamgrenzen. Nicht andersrum. Du beobachtest welche Teams welche Sprache benutzen und machst die implizite Grenze explizit.
Drei Heuristiken:
- Zwei Teams, ein Wort, zwei Bedeutungen: “Kunde” bedeutet für Sales was anderes als für Billing. Das ist eine Context-Grenze.
- Ein Model mit Feldern die nur ein Team nutzt: wenn Shipping nie
leadScoreliest und Sales nietrackingId, hast du zwei Kontexte. - Jede Änderung betrifft mehrere Teams: das ist kein Feature-Problem, sondern eine fehlende Grenze.
Bounded Context ≠ Microservice
Ich sehe das ständig: “Wir haben 5 Bounded Contexts, also bauen wir 5 Microservices.” Nein.
Ein Bounded Context ist eine Modell-Grenze. Nicht eine Deployment-Grenze. Du kannst drei Bounded Contexts in einem Monolithen haben, als Module mit klaren Interfaces. Das ist oft die bessere Wahl als drei Services die sich über HTTP anschreien.
// modules/sales/models.ts
export interface SalesOrder {
id: string;
customerId: string;
discount: number;
campaignCode: string;
}
// modules/billing/models.ts
export interface Invoice {
orderId: string;
taxRates: TaxRate[];
paymentMethod: PaymentMethod;
dunningLevel: number;
}
// modules/shipping/models.ts
export interface Shipment {
orderId: string;
weight: number;
trackingId: string | null;
carrier: Carrier;
}
Drei Module. Drei Models. Ein Monolith. Kein Netzwerk-Overhead, kein verteiltes Tracing, kein Koordinations-Meeting. Und trotzdem: klare Grenzen, eigene Sprache pro Kontext, unabhängige Entwicklung.
“Majestic Monolith” nennt DHH das. Ich bin nicht in allem seiner Meinung, aber hier hat er Recht: die meisten Teams profitieren mehr von einem modularen Monolithen als von Microservices.
Kommunikation zwischen Kontexten
Irgendwann müssen Kontexte miteinander reden. Sales erstellt eine Bestellung, Billing muss eine Rechnung generieren. Drei Optionen:
Published Language: der sendende Kontext definiert ein Event-Schema. OrderPlaced { orderId, customerId, items, total }. Der empfangende Kontext mapped das auf sein eigenes Model.
Anti-Corruption Layer: der empfangende Kontext baut einen Adapter der das fremde Model in sein eigenes übersetzt. Nützlich gegen Legacy-APIs die du nicht kontrollierst.
Shared Kernel: zwei Kontexte teilen ein kleines, gemeinsames Model. Gefährlich. Funktioniert nur wenn beide Teams eng zusammenarbeiten und das Shared Model minimal halten.
// billing/adapters/salesAdapter.ts
import type { SalesOrderEvent } from '../../sales/events';
import type { Invoice } from '../models';
export function toInvoice(event: SalesOrderEvent): Omit<Invoice, 'id'> {
return {
orderId: event.orderId,
taxRates: calculateTaxRates(event.items, event.customerId),
paymentMethod: 'pending',
dunningLevel: 0,
};
}
Der Adapter lebt im Billing-Kontext. Er weiß wie Sales-Events aussehen und übersetzt sie. Wenn Sales sein Event-Format ändert, bricht nur der Adapter, nicht das Billing-Model.
Wann DDD sich nicht lohnt
Wenn dein Team 5 Leute hat und eine CRUD-App baut, brauchst du kein DDD. Du brauchst vernünftige Module und klare Interfaces. DDD lohnt sich wenn:
- Mehrere Teams am selben Codebase arbeiten
- Die Domäne komplex genug ist, dass Begriffe mehrdeutig werden
- Änderungen regelmäßig unerwartete Seiteneffekte haben
Wenn keins davon zutrifft: spar dir die Zeremonie. Gute Architektur ist nicht DDD oder nichts. Es ist ein Spektrum.
Die meisten Projekte brauchen kein Aggregate Root, kein Repository Pattern und kein Domain Event. Was sie brauchen: eine Antwort auf die Frage “wem gehört dieses Konzept?” Wenn du das hast, ist der Rest Details.