Domain-Driven Design in der Praxis: was trägt und was Ballast ist

Domain-Driven Design in der Praxis: was trägt und was Ballast ist

Domain-Driven Design in der Praxis: was trägt und was Ballast ist

Artikel-Übersicht

Domain-Driven Design hat zwei Hälften, und die meisten Projekte übernehmen die falsche. Dieser Artikel trennt sie sauber: die strategische Hälfte mit Ubiquitous Language, Bounded Context und Context Map kostet Gespräche, die taktische mit Entitäten, Value Objects, Aggregaten und Repositories kostet Code. Die These, die der Artikel vertritt und begründet: Die erste zahlt sich fast immer aus, die zweite selten. Danach wird es handwerklich — Schnittkriterien für Bounded Contexts statt Definitionen, die Schnitte, die sich als falsch erweisen, und wie man das merkt. Zwei verbreitete Fehlannahmen werden ausgeräumt: DDD braucht keine Microservices, und es braucht kein Event Sourcing. Zum Schluss die Frage, die in keinem englischsprachigen Text vorkommt und die jeder deutschsprachige Anwender kennt — gehört die deutsche Fachsprache in den Code?

Was Domain-Driven Design ist

Domain-Driven Design ist ein Vorgehen, bei dem die Struktur der Software der Struktur der Fachlichkeit folgt — und die Sprache des Codes der Sprache der Fachleute.

Mehr ist es im Kern nicht. Die Bücher darum herum sind dick, weil sie Muster katalogisieren; die Idee passt in einen Satz.

Was daran neu war, als Eric Evans es 2003 aufschrieb: Die damals übliche Reihenfolge war, ein Datenmodell zu entwerfen und die Fachlichkeit darauf abzubilden. DDD dreht das um. Zuerst wird verstanden, wie die Fachleute denken und sprechen — dann entsteht daraus die Struktur.

Der Preis dafür ist Zeit mit Menschen, nicht mit Werkzeugen. Und genau daran scheitern die meisten DDD-Vorhaben: Sie übernehmen die Muster und lassen die Gespräche weg.

Die zwei Hälften: strategisch und taktisch

Strategisches DDD Taktisches DDD
Bausteine Ubiquitous Language, Bounded Context, Context Map, Subdomains Entity, Value Object, Aggregate, Repository, Domain Event, Factory, Specification
Was es kostet Gespräche, Workshops, Zeit mit Fachleuten Code, Abstraktionen, Pflegeaufwand
Wann es trägt fast immer, sobald mehr als eine Person am System arbeitet wenn es echte fachliche Regeln zu schützen gibt
Was es bewirkt alle meinen dasselbe; Grenzen sind bewusst gezogen Invarianten sind im Code durchgesetzt
Was passiert, wenn man es weglässt Missverständnisse, die sich als Bugs zeigen mehr Prozeduren, weniger Modell — oft völlig in Ordnung

Die Aussage der letzten Zeile ist die These dieses Artikels: Wer die strategische Hälfte weglässt, bekommt ein System, in dem Fachbereich und Entwicklung aneinander vorbeireden. Wer die taktische Hälfte weglässt, bekommt oft einfach schlankeren Code.

Das ist kein Freibrief. Es gibt Fachlichkeiten mit echten Invarianten — Versicherungstarife, Buchhaltung, Tarifrecht —, wo ein Aggregat, das Regeln durchsetzt, seinen Preis wert ist. Aber in einem System, das im Wesentlichen Formulare auf Tabellen abbildet, sind Aggregate eine Zeremonie ohne Empfänger.

Ubiquitous Language: der Teil, der immer lohnt

Ubiquitous Language heißt: ein Begriff, eine Bedeutung, überall — in der Besprechung, im Ticket, im Klassennamen, in der Datenbankspalte.

Das klingt banal und ist die wirksamste einzelne Maßnahme aus dem ganzen Werkzeugkasten. Der Grund: Übersetzungsfehler zwischen Fachbereich und Code sind unsichtbar. Ein Code, der status heißt, wo der Fachbereich zwischen Bearbeitungsstand und Freigabestand unterscheidet, funktioniert fehlerfrei — bis jemand die beiden verwechselt, und das tut irgendwann jeder.

Woran man merkt, dass die Sprache auseinanderläuft:

  • Im Gespräch mit dem Fachbereich muss jemand ständig übersetzen („was ihr Vorgang nennt, heißt bei uns Ticket").
  • Derselbe Begriff bedeutet in zwei Teilen des Systems Verschiedenes.
  • Es gibt ein Glossar, das niemand pflegt.
  • Neue Teammitglieder stellen dieselbe Verständnisfrage wie ihre Vorgänger.

Die Gegenmaßnahme ist unspektakulär: Begriffe im Gespräch klären, im Code verwenden, und Abweichungen im Review benennen. Ein Glossar hilft, ersetzt das aber nicht.

Bounded Context: wie man schneidet

Ein Bounded Context ist ein Bereich, in dem ein Modell und seine Begriffe eindeutig gelten. Außerhalb gelten andere.

Das ist der Kern des strategischen DDD, und es ist der Punkt, an dem die Definition weniger hilft als die Kriterien. Woran schneidet man?

Unterschiedliche Sprache. Der zuverlässigste Indikator. Wenn derselbe Begriff in zwei Bereichen Verschiedenes bedeutet, verläuft dort eine Grenze. Eine Rechnung im Vertrieb ist etwas anderes als eine Rechnung in der Buchhaltung — im Vertrieb ist sie das Ergebnis eines Abschlusses, in der Buchhaltung ein Beleg mit Fristen. Beide Modelle sind richtig. Sie gehören nur nicht in dieselbe Klasse.

Unterschiedliche Änderungsrhythmen. Wenn ein Teil monatlich fachlich geändert wird und ein anderer seit drei Jahren stabil ist, ist das eine Grenze.

Unterschiedliche Verantwortliche. Wenn zwei Bereiche verschiedene fachliche Ansprechpartner haben, die einander nicht abstimmen müssen, folgt daraus fast immer eine sinnvolle Trennung.

Und eine Warnung: Technische Kriterien sind keine Schnittkriterien. „Alles, was auf dieselbe Tabelle zugreift" oder „alles, was in derselben Schicht liegt" ergibt keine Bounded Contexts, sondern eine Aufteilung nach Zufall.

Die Schnitte, die sich als falsch erweisen

Der wertvollste Teil dieses Abschnitts: Woran merkt man, dass ein Schnitt falsch war?

Drei Symptome, in der Reihenfolge, in der sie auftreten:

Erstens: Jede Änderung berührt zwei Kontexte. Wenn eine fachliche Anforderung regelmäßig Anpassungen an beiden Seiten einer Grenze erfordert, verläuft die Grenze quer durch einen Zusammenhang statt zwischen zweien.

Zweitens: Es entsteht ein Übersetzungsschicht-Wildwuchs. Wenn zwischen zwei Kontexten immer mehr Abbildungslogik entsteht, die nichts fachlich Sinnvolles tut, war die Trennung an dieser Stelle künstlich.

Drittens: Ein Kontext hat keine eigene Sprache. Wenn ein Bereich nur Begriffe verwendet, die aus einem anderen stammen, ist er kein eigener Kontext, sondern ein Modul davon.

Das erste Symptom ist das früheste und wird am häufigsten übersehen, weil es sich wie normale Arbeit anfühlt.

DDD und Microservices

Die verbreitetste Fehlannahme im Umfeld von DDD, und sie hat eine einfache Auflösung:

Ein Bounded Context ist eine logische Grenze. Ein Microservice ist eine Deployment-Einheit. Das eine erzwingt das andere nicht.

Dass beide Begriffe zusammen genannt werden, hat einen sachlichen Kern: Wenn man ein System verteilt, sind Bounded Contexts die besten Schnittlinien, die man finden kann. Umgekehrt gilt das nicht. Man kann fünf Bounded Contexts in einem einzigen Deployment betreiben — als Module mit erzwungenen Grenzen —, und das ist für die meisten Teams die bessere Wahl.

Der Satz, der das zusammenfasst: Ein Monolith mit sauberen Kontexten schlägt verteilte Kontexte ohne Grenzen. Im ersten Fall hat man ein Refactoring vor sich, wenn der Schnitt falsch war. Im zweiten eine Migration.

Braucht DDD Event Sourcing und CQRS?

Nein.

Die Verbindung ist historisch entstanden, nicht sachlich: Dieselbe Community hat in denselben Jahren über beides geschrieben, dieselben Konferenzen haben beides behandelt, und mehrere einflussreiche Bücher behandeln beides zusammen. Daraus ist der Eindruck entstanden, das eine gehöre zum anderen.

Tatsächlich sind es getrennte Entscheidungen:

  • DDD ist eine Frage der Modellierung und der Sprache.
  • CQRS trennt Lese- und Schreibmodell. Das lohnt, wenn beide stark unterschiedliche Anforderungen haben — etwa wenn gelesen tausendfach häufiger wird als geschrieben.
  • Event Sourcing speichert Zustandsänderungen statt Zustand. Das lohnt, wenn die Historie fachlich gefordert ist — Buchhaltung, Auditpflicht, Nachvollziehbarkeit von Entscheidungen.

Beides sind mächtige Muster mit erheblichen Folgekosten: Event Sourcing verlangt eine Antwort auf Schemaänderungen alter Ereignisse, auf Auswertungen über die Historie und auf die Frage, wie man einen Fehler korrigiert, der vor zwei Jahren passiert ist. Wer das ohne fachlichen Anlass einführt, weil es zu DDD zu gehören scheint, kauft ein zweites Projekt.

Was von den taktischen Bausteinen bleibt

Ehrlich, nach Nutzen sortiert:

Value Objects lohnen fast immer. Sie sind billig — eine kleine unveränderliche Klasse mit Gleichheit über den Wert — und sie beseitigen eine ganze Fehlerklasse. Eine Rechnungsnummer als eigener Typ kann nicht versehentlich mit einer Kundennummer vertauscht werden. Wer nur einen einzigen taktischen Baustein übernimmt, nimmt diesen.

Aggregate lohnen bei echten Invarianten. Ein Aggregat ist eine Konsistenzgrenze: Innerhalb gilt eine Regel immer. Wenn es eine solche Regel gibt — „die Summe der Positionen entspricht dem Rechnungsbetrag", „ein Vertrag hat immer genau einen gültigen Tarif" —, ist das Aggregat der richtige Ort dafür. Wenn es keine gibt, ist es eine Klasse, die andere Klassen kapselt, ohne etwas durchzusetzen.

Repositories lohnen, wenn mehr als eine Quelle existiert oder wenn Tests ohne Datenbank laufen sollen. Als reine Umbenennung des ORM-Zugriffs sind sie eine Schicht ohne Inhalt.

Domain Events lohnen bei mehreren Kontexten. Innerhalb eines Kontexts ist ein direkter Methodenaufruf klarer als ein Ereignis, das irgendwo verarbeitet wird.

Factories und Specifications sind meist Zeremonie. Es gibt Fälle für beide — komplexe Erzeugung mit Regeln, wiederverwendbare Filterkriterien —, aber sie sind seltener, als die Bücher nahelegen.

Wie sich diese Bausteine in einer Ordnerstruktur niederschlagen und warum die Abhängigkeitsrichtung dabei wichtiger ist als die Anzahl der Schichten, steht in Clean Architecture: Schichten, Grenzen und die Frage, was man weglässt.

Wann DDD nicht lohnt

  • Kurze Projektlaufzeit. Der Nutzen entsteht über Jahre.
  • Dünne Fachlichkeit. Wenn das System im Kern Formulare auf Tabellen abbildet, gibt es kein Modell zu entdecken.
  • Kein Zugang zu Fachleuten. Das ist der härteste Punkt: DDD ohne Gesprächspartner in der Fachabteilung ist nicht umsetzbar. Wer die Fachlichkeit nur aus Tickets kennt, kann keine Ubiquitous Language entwickeln — es bleibt bei Vokabular ohne gemeinsames Verständnis. Das ist der häufigste Grund, warum DDD-Vorhaben scheitern, und er ist kein technischer.

Wie man anfängt

Nicht mit einem Framework und nicht mit einer Ordnerstruktur. Zwei Möglichkeiten, beide kosten einen Tag:

Ein Begriffsglossar. Zwanzig Begriffe, die im Fachbereich vorkommen, mit je einem Satz Bedeutung — geschrieben von den Fachleuten, nicht von der Entwicklung. Danach vergleicht man mit den Namen im Code. Die Abweichungen sind die Liste der Missverständnisse, die man bisher hatte.

Event Storming. Ein Workshop, in dem alle Beteiligten die fachlichen Ereignisse eines Ablaufs auf Klebezettel schreiben und in zeitliche Reihenfolge bringen. Das Verfahren stammt von Alberto Brandolini und braucht nichts außer einer Wand, Zetteln und den richtigen Leuten im Raum. Nach drei Stunden sieht man Grenzen, die vorher niemand benennen konnte.

Beides erzeugt keinen Code. Genau das ist der Punkt.

Deutsch oder Englisch im Code?

Die Frage, die im gesamten englischsprachigen Schrifttum nicht vorkommt und die im deutschsprachigen Raum jeder kennt.

Die reine Lehre ist eindeutig: Die Ubiquitous Language ist die Sprache der Fachleute. Wenn der Fachbereich von Vorsteuerabzug, Betriebsrat und Verkehrssicherungspflicht spricht, dann heißen die Klassen so. Eine Übersetzung ins Englische ist genau der Bruch, den DDD vermeiden will — und bei vielen deutschen Rechts- und Verwaltungsbegriffen gibt es schlicht keine tragfähige Entsprechung.

Die Praxis ringt damit, aus nachvollziehbaren Gründen: gemischtsprachige Bezeichner lesen sich unschön (calculateVorsteuerabzug), Umlaute in Bezeichnern sind je nach Sprache und Werkzeugkette unangenehm, und internationale Teams stoßen an eine Verständnisgrenze.

Meine Empfehlung nach mehreren Projekten:

  • Fachbegriffe deutsch, technische Begriffe englisch. Vorsteuerabzug, Mahnstufe, Beitragsbemessungsgrenze bleiben deutsch. Repository, Service, Controller, Factory bleiben englisch. Die Trennlinie verläuft genau dort, wo auch die Architekturgrenze verläuft — innen die Fachsprache, außen die Technik.
  • Keine Umlaute in Bezeichnern. Vorsteuerabzug ja, Beitragsüberschuss besser als Beitragsueberschuss — aber wenn die Werkzeugkette Probleme macht, ist die Umschreibung das kleinere Übel als die Übersetzung.
  • Nicht auf halbem Weg stehenbleiben. Ein Modell mit Rechnung, Invoice und Beleg nebeneinander ist schlechter als jede der drei Varianten konsequent.

Das ist eine Empfehlung aus der Praxis, keine Regel aus dem Buch. Aber sie beantwortet eine Frage, die sich in jedem deutschsprachigen DDD-Projekt in der ersten Woche stellt.

Häufige Fragen

Was ist Domain-Driven Design? Ein Vorgehen, bei dem die Struktur der Software der Struktur der Fachlichkeit folgt und die Sprache des Codes der Sprache der Fachleute.

Was ist der Unterschied zwischen strategischem und taktischem DDD? Strategisch sind Ubiquitous Language, Bounded Context und Context Map — sie kosten Gespräche. Taktisch sind Entity, Value Object, Aggregate, Repository, Domain Event, Factory und Specification — sie kosten Code.

Was ist ein Bounded Context? Ein Bereich, in dem ein Modell und seine Begriffe eindeutig gelten. Geschnitten wird an unterschiedlicher Sprache, unterschiedlichen Änderungsrhythmen und unterschiedlichen Verantwortlichen.

Braucht DDD Microservices? Nein. Ein Bounded Context ist eine logische Grenze, ein Microservice eine Deployment-Einheit. Mehrere Kontexte in einem Deployment sind für die meisten Teams die bessere Wahl.

Braucht DDD Event Sourcing? Nein. Die Verbindung ist historisch, nicht sachlich. Event Sourcing lohnt, wenn die Historie fachlich gefordert ist — sonst kauft man sich ein zweites Projekt.

Wann lohnt sich DDD nicht? Bei kurzer Laufzeit, dünner Fachlichkeit und vor allem dann, wenn es keinen Zugang zu Fachleuten gibt. Der letzte Punkt ist ein Ausschlusskriterium, kein Erschwernis.

Zusammenfassung und praktische Tipps

Domain-Driven Design zerfällt in zwei Hälften mit sehr unterschiedlichem Nutzen-Kosten-Verhältnis. Die wichtigsten Punkte:

  • Strategisches DDD trägt fast immer, taktisches selten. Die Sprache und der fachliche Schnitt sind der Gewinn; die Musterkataloge sind es meist nicht.
  • Ubiquitous Language ist die wirksamste Einzelmaßnahme. Ein Begriff, eine Bedeutung, überall.
  • Bounded Contexts schneidet man an der Sprache, nicht an der Technik.
  • Wenn jede Änderung zwei Kontexte berührt, war der Schnitt falsch — und das ist das früheste Symptom.
  • DDD braucht weder Microservices noch Event Sourcing. Beides sind getrennte Entscheidungen mit eigenen Kosten.
  • Value Objects sind der eine taktische Baustein, den man immer mitnimmt.
  • Ohne Fachleute im Raum ist DDD nicht umsetzbar. Das ist kein Aufwandsproblem, sondern ein Ausschlusskriterium.

Mein Rat für den Einstieg: Nimm dir einen Tag und schreib mit den Fachleuten zwanzig Begriffe auf, jeweils mit einem Satz Bedeutung. Vergleich das anschließend mit den Namen in eurem Code. Diese eine Liste zeigt dir mehr über euer System als jedes Architekturdiagramm — und sie kostet weniger als ein einziges Refactoring, das aus einem Missverständnis entstanden ist.

Ressourcen