Frag in einem mittelständischen Maschinenbauer drei Abteilungsleiter, was ein Auftrag ist, und du bekommst drei Antworten. Für den Vertrieb ist es ein angenommenes Angebot mit Preis und Rabatt. Für die Produktion ein Fertigungsauftrag mit Stückliste. Für den Service ein Reparaturfall an einer Maschine, die vor sieben Jahren ausgeliefert wurde. Die Software kennt genau eine Tabelle namens auftrag, und in ihr leben alle drei Bedeutungen gleichzeitig.
Ich behaupte: An diesem einen Wort kippen mehr Softwareprojekte als an der Framework-Wahl. Domain-Driven Design ist die Methode, die genau dieses Problem ernst nimmt. Viele halten DDD für Architektur-Theorie für Konzerne mit hundert Entwicklern. Ich halte das für ein Missverständnis: Im Mittelstand ist DDD eher wichtiger, weil das Fachwissen in wenigen Köpfen steckt und die Teams zu klein sind, um falsch geschnittene Software jahrelang mitzuschleppen.
Meine Faustregel: erst die Sprache und die Grenzen klären, Code-Muster nur dort einsetzen, wo das Geschäft wirklich kompliziert ist. Die eine Hälfte der DDD-Literatur entscheidet darüber, ob in zehn Jahren noch jemand versteht, was das Feld status2 bedeutet. Die andere Hälfte kannst du für den Anfang liegen lassen.
Was ist Domain-Driven Design?
Eric Evans hat 2003 ein ganzes Buch über genau diese Tabelle geschrieben, auch wenn sie bei ihm anders heißt: „Domain-Driven Design: Tackling Complexity in the Heart of Software". Seine These: Die schwierigste Komplexität in Unternehmenssoftware steckt in der Fachdomäne, also in den Regeln, Ausnahmen und Abhängigkeiten des Geschäfts. Wer diese Domäne unsauber im Code abbildet, baut Software, bei der jede neue Anforderung teurer wird als die letzte.
DDD liefert dafür zwei Werkzeugkästen. Das strategische Design beantwortet, wie du ein großes System in sinnvolle Bereiche aufteilst und wie diese Bereiche miteinander reden. Das taktische Design liefert Muster für den Code innerhalb eines Bereichs: Entities, Value Objects, Aggregates, Repositories, Domain Events. Viele Einführungen beginnen mit dem taktischen Kasten, auch Evans' Buch selbst. Für den Mittelstand bringt der strategische mehr.
Zwei Begriffe tragen das ganze Konzept. Die Ubiquitous Language ist die gemeinsame Fachsprache, die Fachabteilung und Entwicklung identisch benutzen, im Meeting genauso wie im Klassennamen. Ein Bounded Context ist die Grenze, innerhalb derer diese Sprache eindeutig gilt. Außerhalb darf dasselbe Wort etwas anderes bedeuten, und das ist ausdrücklich erlaubt.
Kurz gesagt: DDD akzeptiert, dass es kein einheitliches Unternehmensmodell gibt, und macht daraus eine Architekturregel.
Strategisches Design: Bounded Contexts und Subdomänen
Der klassische Reflex in gewachsener Software ist, alle Bedeutungen eines Begriffs in ein gemeinsames Datenmodell zu pressen. Das Ergebnis kennt jeder IT-Leiter: eine Tabelle mit 140 Spalten, von denen jede Abteilung 30 nutzt, und Felder wie status2 oder liefertermin_neu, deren Bedeutung niemand mehr erklären kann. Evans hält eine vollständige Vereinheitlichung des Domänenmodells für ein großes System ausdrücklich für weder machbar noch wirtschaftlich, wie Martin Fowler in seiner Erklärung des Bounded Context zusammenfasst.
| Begriff | Vertrieb | Produktion | Service |
|---|---|---|---|
| Auftrag | Angenommenes Angebot mit Preis und Rabatt | Fertigungsauftrag mit Stückliste und Kapazität | Reparatur- oder Wartungsfall |
| Kunde | Ansprechpartner mit Potenzial | Spielt kaum eine Rolle | Betreiber einer installierten Maschine |
| Artikel | Verkaufbares Produkt mit Konfiguration | Baugruppe mit Arbeitsplan | Ersatzteil mit Kompatibilitätsliste |
DDD gibt jedem dieser Bereiche einen eigenen Bounded Context mit eigenem Modell. Der Vertriebs-Auftrag kennt Rabatte, aber keine Arbeitspläne. Der Fertigungsauftrag kennt Kapazitäten, aber keine Zahlungsbedingungen. Wo Kontexte Daten austauschen, übersetzt eine definierte Schnittstelle sie ins jeweilige Modell. Kommt später die Buchhaltung mit ihrer Sicht auf Rechnungen und Debitoren dazu, bekommt sie einen weiteren Kontext, und ihre Felder tauchen nie in der Fertigung auf.
Der zweite strategische Schritt ist die Einteilung in Subdomänen. Evans unterscheidet die Kerndomäne (Core Domain), in der sich dein Unternehmen vom Wettbewerb abhebt, von generischen Subdomänen, die jedes Unternehmen gleich braucht. Vaughn Vernon hat 2013 in „Implementing Domain-Driven Design" eine dritte Kategorie etabliert: unterstützende Subdomänen, die spezifisch für dich sind, aber keinen Wettbewerbsvorteil stiften. Beim Maschinenbauer mit Produktkonfigurator wäre die Kerndomäne die Variantenlogik, also die Regeln, welche Baugruppen zusammenpassen. Die Serviceeinsatzplanung wäre unterstützend, Finanzbuchhaltung und Lohn wären generisch.
Diese Einteilung ist der eigentliche Wert von DDD für ein Budget-Gespräch. Sie zeigt dir, wo eigene Entwicklung Geld verdient und wo sie Geld verbrennt.
Warum DDD im Mittelstand mehr bringt als im Konzern
Laut Statistischem Bundesamt erreichen bis 2039 rund 13,4 Millionen Erwerbspersonen das gesetzliche Rentenalter, knapp ein Drittel der Erwerbspersonen von 2024. In vielen mittelständischen Unternehmen gehen mit ihnen Geschäftsregeln, die nirgends aufgeschrieben sind: Der Innendienstler weiß, welche Optionen sich technisch ausschließen, die Disponentin kennt die Sonderregeln für Großkunden auswendig.
DDD zwingt dieses Wissen in eine explizite Sprache und in Code, der es lesbar abbildet. So wird aus Wissen, das in Rente gehen kann, ein Begriff im Glossar und eine Regel an einer auffindbaren Stelle.
Im Zentrum der Systemlandschaft steht fast immer ein ERP-System, um das herum über Jahre Individualsoftware, Excel-Makros und Schnittstellen gewachsen sind. Ohne klare Grenzen sickert das Datenmodell des ERP in jede Eigenentwicklung: Dieselben kryptischen Feldnamen und Statuscodes tauchen im Kundenportal, in der Service-App und im Excel-Export auf, und verstehen kann sie nur, wer das ERP-Customizing kennt. Ein Bounded Context mit eigener Übersetzungsschicht bündelt diese Abhängigkeit an genau einer Stelle.
Dazu kommt die Teamgröße. Viele Mittelständler beschäftigen eine Handvoll Entwickler, intern oder bei einer Agentur. Melvin Conway formulierte schon 1968, dass Organisationen Systeme entwerfen, die ihre eigenen Kommunikationsstrukturen kopieren. Bei fünf Entwicklern ohne klare Zuständigkeiten wird die Software so verschachtelt wie die Absprachen zwischen ihnen.
Das Ergebnis ist ein vertrautes Bild: Der einzige Kollege, der die Preisfindung versteht, ist zwei Wochen im Urlaub, und niemand traut sich an die Tabelle auftrag. Klare Kontextgrenzen machen es möglich, dass eine Person einen Bereich verantwortet, ohne das ganze System im Kopf haben zu müssen.
Wo sich DDD nicht lohnt
Jetzt der unbequeme Teil. Wer DDD ernst nimmt, setzt es gezielt ein. Evans empfiehlt, den größten Modellierungsaufwand und die besten Leute auf die Kerndomäne zu konzentrieren.
Für generische Subdomänen ist eigene Entwicklung meist die falsche Entscheidung. Für Finanzbuchhaltung, Lohnabrechnung und Zeiterfassung gibt es ausgereifte Standardsoftware, und kein Kunde kauft bei dir, weil deine Lohnabrechnung besonders elegant modelliert ist. Die Abwägung dahinter haben wir im Beitrag SaaS vs. Individualsoftware ausführlich durchgerechnet.
Auch reine Datenpflege-Anwendungen brauchen kein DDD. Ein internes Tool, das Stammdaten anzeigt und speichert, hat kaum Geschäftsregeln. Aggregates und Domain Events wären dort reiner Overhead, ein schlichtes CRUD-Backend reicht völlig.
| Kriterium | Spricht für DDD | Spricht dagegen |
|---|---|---|
| Geschäftsregeln | Viele Regeln, Ausnahmen, Zustandswechsel | Vor allem Daten anzeigen und speichern |
| Wettbewerbsrelevanz | Kerndomäne, die dich vom Wettbewerb abhebt | Generischer Prozess, den jeder gleich macht |
| Lebensdauer | System soll zehn Jahre und länger halten | Kurzlebiges Tool oder Prototyp |
| Fachwissen | Steckt bei wenigen Mitarbeitenden | Ist vollständig dokumentiert oder trivial |
| Beteiligte Abteilungen | Mehrere Abteilungen mit eigener Sicht | Ein Team, eine Sicht |
Wenn drei oder mehr Zeilen links landen, lohnt sich zumindest das strategische Design. Taktische Muster brauchst du nur für die Teile, die wirklich komplex sind.
DDD in der Praxis: So startest du in vier Schritten
Am Anfang steht ein Raum mit Fachleuten und einer großen Wand. Klassen und Ordnerstrukturen kommen später. Die vier Schritte folgen dem Maschinenbauer aus der Einleitung.
Schritt 1: Event Storming mit Fachabteilung und Entwicklung
Event Storming ist ein Workshop-Format, das Alberto Brandolini 2013 vorgestellt hat. Fachleute und Entwickler kleben gemeinsam Domain Events auf orange Haftnotizen, formuliert in der Vergangenheit: „Angebot angenommen", „Konfiguration geprüft", „Fertigungsauftrag freigegeben", „Maschine ausgeliefert". Die Ereignisse werden entlang einer Zeitachse sortiert. Danach kommen die Befehle, die sie auslösen, auf blaue Notizen. Offene Fragen und Konflikte markiert das Team als Hotspots.
Den eigentlichen Wert liefern die Diskussionen. Wenn der Vertriebsleiter und die Leiterin der Arbeitsvorbereitung zehn Minuten darüber streiten, ob „Auftrag bestätigt" vor oder nach der technischen Machbarkeitsprüfung passiert, hast du eine Kontextgrenze gefunden. Meist haben beide recht, jeder in seinem Kontext, und genau dort verläuft die Grenze.
Ein Workshop für einen Kernprozess dauert typischerweise ein bis zwei Tage. Er macht die spätere Anforderungsdokumentation deutlich präziser; wie du die Ergebnisse in ein belastbares Dokument überführst, steht im Leitfaden zu Lastenheft und Pflichtenheft.
Ein Workshop ohne Fachleute ist dabei wertlos. Ein typisches Scheitermuster: Das Team liest Evans, benennt Ordner in domain, application und infrastructure um und spricht danach trotzdem nicht mit der Fachabteilung. Heraus kommt neuer Entwickler-Jargon, den außerhalb des Teams niemand versteht.
Schritt 2: Die Kerndomäne benennen
Aus dem Workshop entsteht eine Landkarte der Subdomänen. Jetzt kommt die Frage, die Geschäftsführung und IT gemeinsam beantworten müssen: Wo verdienen wir unser Geld? Im Workshop sagt meist zuerst jemand „überall". Nach einer halben Stunde Diskussion bleibt fast immer ein einzelner Bereich übrig. Beim Maschinenbauer ist es die Variantenkonfiguration. Bei einem Großhändler wäre es vielleicht die Preisfindung für Rahmenverträge, bei einem Logistiker die Disposition.
Für diesen Bereich lohnen sich das beste Team und die gründlichste Modellierung. Für den Rest gilt: kaufen, integrieren oder so schlank wie möglich selbst bauen. Diese Priorisierung ist auch das Fundament jeder Make-or-Buy-Entscheidung.
Schritt 3: Bounded Contexts schneiden und die Context Map zeichnen
Jetzt werden die Grenzen festgelegt. Jeder Bounded Context bekommt ein Glossar, das seine Ubiquitous Language festhält: Was dort „Fertigungsauftrag" heißt, heißt auch im Code so, nicht ProdOrder oder fa_tmp.
Das Glossar lebt allerdings nur, solange das Team es benutzt. Wer ein halbes Jahr nicht hinschaut, findet plötzlich Auftrag und Order nebeneinander im selben Modul, und niemand weiß mehr, ob das zwei Dinge sind oder eins. Neue Begriffe gehören deshalb in Tickets, Code-Reviews und Meetings und werden geändert, sobald die Fachabteilung anders spricht.
Danach zeichnest du, wie die Kontexte zusammenhängen. DDD nennt das Context Map. Ein Muster brauchst du dabei fast immer: den Anticorruption Layer. Er sitzt zwischen deinem Kontext und dem ERP und übersetzt dessen Datenmodell in deine Sprache. Stell dir vor, ein ERP-Release benennt ein Feld um. Ohne Übersetzungsschicht suchst du die Stelle im ganzen System. Mit ihr ist es eine Klasse, ein Test, ein Deployment, und die Fachlogik bleibt unberührt.
Schritt 4: Modularer Monolith statt Microservices
An dieser Stelle stolpern viele Teams ein zweites Mal. Sie setzen Bounded Context mit Microservice gleich und starten mit zwölf Services, zwölf Deployments und einem Message Broker. Für ein Team von fünf Leuten ist das betriebliche Selbstverletzung. Martin Fowler riet schon 2015 in seinem Artikel „MonolithFirst", neue Projekte nicht mit Microservices zu beginnen: Stabile Grenzen sind am Anfang kaum richtig zu ziehen, und Refactoring über Servicegrenzen hinweg ist viel teurer als innerhalb einer Codebasis.
Die bessere Zielarchitektur für die meisten Mittelständler ist ein modularer Monolith: eine Anwendung, ein Deployment, intern aber streng getrennte Module, eines pro Bounded Context, die nur über definierte Schnittstellen miteinander reden.
In einem NestJS-Backend bildest du jeden Kontext als eigenes Modul mit eigenem Datenbankschema ab. Innerhalb eines Moduls trennt eine hexagonale Architektur (Ports and Adapters) die Fachlogik von Datenbank und Schnittstellen. Wird ein Modul später wirklich zum Engpass, löst du es als eigenen Service heraus. Die Grenze existiert dann schon. Mehr zur Backend-Seite steht im Beitrag zur Enterprise-Backend-Architektur.
Shopify ist das bekannteste Beispiel für diesen Weg. Das Unternehmen beschrieb 2019, wie es seine riesige Rails-Codebasis zu einem modularen Monolithen umbaut. Lehrreicher ist die Retrospektive von 2024 zum dafür gebauten Prüfwerkzeug Packwerk: Die nach fachlichen Domänen gezogenen Paketgrenzen passten oft nicht dazu, wie der Code zur Laufzeit tatsächlich zusammenhängt. Shopify definiert Pakete seitdem danach, wie der Code tatsächlich voneinander abhängt, und nutzt die fachlichen Domänen als übergeordnete Klammer, damit Teams sie verantworten können.
Werkzeuge können Grenzen prüfen, aber nicht finden.
Das spricht nicht gegen DDD. Es spricht gegen den Glauben, der erste Schnitt sei schon der richtige. Der Schnitt aus dem Workshop ist eine Hypothese, die sich am echten Code bewähren muss. Im Monolithen kostet ein falscher Schnitt einen Refactoring-Sprint. Zwischen zwei Services kostet er eine Migration mit Datenumzug.
Für die Modernisierung einer gewachsenen Anwendung heißt das: einen Bounded Context nach dem anderen aus dem Altsystem herauslösen, nach dem Strangler-Fig-Pattern. Wie das abläuft, haben wir im Beitrag App-Update oder Neubau beschrieben; für Backends funktioniert es genauso.
Taktisches DDD: nur dort, wo es zählt
Innerhalb der Kerndomäne lohnen sich die taktischen Muster, weil sie Geschäftsregeln in die Fachobjekte selbst legen. In gewachsener Software findest du dieselbe Preisregel oft in drei Controllern und einem Datenbank-Trigger. Zwei Muster bringen den größten Nutzen.
Ein Value Object ist ein Wert, der seine Regeln selbst mitbringt: Ein Liefertermin muss zum Beispiel in der Zukunft liegen. Ein Aggregate ist ein Verbund von Objekten, der nur über eine Wurzel, die Aggregate Root, verändert werden darf. In einfachen Worten: Der Auftrag passt selbst auf seine Positionen auf. Niemand schiebt von außen eine Position in einen bestätigten Auftrag, weil der einzige Weg hinein über den Auftrag führt, und der sagt Nein. In TypeScript sieht das so aus:
type Position = { artikelNr: string; menge: number };
// Value Object: Ein Liefertermin prüft seine Regeln selbst
export class Liefertermin {
private constructor(private readonly wert: Date) {}
static am(datum: Date, heute = new Date()): Liefertermin {
if (datum <= heute) {
throw new Error('Liefertermin muss in der Zukunft liegen');
}
return new Liefertermin(new Date(datum));
}
get datum(): Date {
return new Date(this.wert);
}
}
// Aggregate Root: Nur der Auftrag selbst ändert seine Positionen
export class Auftrag {
private positionen: Position[] = [];
private status: 'offen' | 'bestaetigt' = 'offen';
private termin?: Liefertermin;
positionHinzufuegen(position: Position): void {
this.pruefeOffen();
this.positionen.push({ ...position });
}
bestaetigen(termin: Liefertermin): void {
this.pruefeOffen();
if (this.positionen.length === 0) {
throw new Error('Ein leerer Auftrag kann nicht bestätigt werden');
}
this.status = 'bestaetigt';
this.termin = termin;
}
get liefertermin(): Liefertermin | undefined {
return this.termin;
}
private pruefeOffen(): void {
if (this.status === 'bestaetigt') {
throw new Error('Bestätigte Aufträge sind gesperrt');
}
}
}
Der Code liest sich fast wie die Sätze, die im Workshop gefallen sind. Wenn die Disponentin sagt, ein bestätigter Auftrag dürfe nicht mehr verändert werden, findet ein neuer Entwickler diese Regel an einer einzigen Stelle.
Was du nicht brauchst, ist Zeremonie. Repositories sind ein DDD-Muster, aber Evans sieht sie pro Aggregate vor, nicht pro Tabelle. CQRS und Event Sourcing sind eigenständige Architekturoptionen für spezielle Probleme wie stark unterschiedliche Lese- und Schreiblasten oder lückenlose Änderungshistorien. Setz sie ein, wenn du dieses Problem wirklich hast.
Ein Nebeneffekt gewinnt gerade an Gewicht: KI-Coding-Agenten. Arbeitet ein Agent in einem klar geschnittenen Modul mit eindeutigen Begriffen, muss er nicht raten, was status2 bedeuten könnte. Das Glossar pro Bounded Context kannst du ihm direkt als Arbeitsgrundlage mitgeben.
FAQ zu Domain-Driven Design
Was ist Domain-Driven Design, einfach erklärt?
DDD ist ein Ansatz, Software entlang der Fachlichkeit eines Unternehmens zu bauen. Entwickler und Fachabteilung entwickeln eine gemeinsame Sprache, teilen das System in klar abgegrenzte Bereiche und investieren den größten Aufwand dort, wo sich das Geschäft vom Wettbewerb unterscheidet.
Ist DDD dasselbe wie Microservices?
Nein. Bounded Contexts sind gute Kandidaten für Servicegrenzen, DDD funktioniert aber genauso in einer einzigen Anwendung.
Wie lange dauert ein Event-Storming-Workshop?
Für einen einzelnen Kernprozess sind ein bis zwei Tage typisch. Wie schnell danach das erste sauber geschnittene Modul produktiv ist, hängt vom Zustand des Altsystems und der Verfügbarkeit der Fachleute ab.
Lohnt sich DDD für kleine Teams?
Das strategische Design lohnt sich, sobald mehrere Abteilungen mit eigener Sicht beteiligt sind, auch bei zwei Entwicklern.
Fazit: Erst die Sprache, dann die Architektur
Die Tabelle auftrag aus der Einleitung verschwindet nicht durch ein neues Framework. Sie verschwindet, wenn Vertrieb, Produktion und Service jeweils ihr eigenes Modell bekommen und das Team ihre Sprache spricht. Erst dann siehst du auch, welches dieser Modelle dein Geld verdient und welches du einfach kaufen kannst.
Meine Empfehlung: Starte mit einem Event-Storming-Workshop für den Prozess, der deinem Unternehmen am meisten Geld bringt oder am meisten Ärger macht. Benenne die Kerndomäne, schneide zwei, drei Bounded Contexts und baue sie als Module in einer Anwendung. Taktische Muster kommen dazu, wenn die Regeln es verlangen.
Wenn du eine gewachsene Individualsoftware neu schneiden oder ein neues System von Anfang an sauber aufsetzen willst, sprich mit uns über deine Enterprise-Software.