Start / Cheat Sheets

Cheat Sheet

Domain-Driven Design — Cheat Sheet

Stand: · Domain-Driven Design in der Praxis

Domain-Driven DesignSoftwarearchitekturBounded ContextAggregate

Der Spickzettel zum Seminar Domain-Driven Design in der Praxis — für den Entwurfstermin, das Review oder den Moment, in dem jemand fragt, ob das jetzt eine Entity oder ein Value Object ist. Zum Lernen der Methode taugt er nicht; dafür ist das Seminar da. Jeder Abschnitt verweist auf sein Quellmodul.

Wann DDD trägt

DDD wirkt gegen fachliche Komplexität — und nur dagegen.

Dominierendes AttributTypisches ProjektDDD hilft?
DatenmengeAuswertung großer Sensorströmenein
PerformanceHandelsplattform, Latenz in Millisekundennein
Fachliche KomplexitätERP-System, Unternehmensprozesseja
Technische KomplexitätSteuersoftware für eingebettete Systemenein

Die drei strategischen Muster

MusterFrage, die es beantwortet
Allgegenwärtige SpracheWie nennen Fachbereich und Code dieselbe Sache?
Bounded ContextWo endet die Gültigkeit eines Modells?
KerndomäneWelcher Teil rechtfertigt die meiste Sorgfalt?

Nicht jede Fachlogik ist Kerndomäne — Buchhaltung kauft man zu.

Modul: Einführung

Die Ringe der Onion-Architektur

RingEnthältBauform Desktop / Web
OberflächeBildschirme, Eingaben, DarstellungAnsichten / Templates
AnwendungsservicesAbläufe, Transaktionen, OrchestrierungViewModels / Controller
Repositories, Factories, Domain ServicesZugriff, Erzeugung, übergreifende Regelndieselben
KernEntity, Value Object, Aggregate, Domain Eventdieselben

Die Datenbank fehlt in der Aufzählung — sie steckt hinter den Repositories. Der Ring entscheidet über die Zuständigkeit, nicht das Framework: Ergibt eine Logik nur mit Blick auf den Bildschirm Sinn, gehört sie in den Anwendungsservice; gilt sie unabhängig von jeder Anzeige, in den Kern.

Module: Einführung · UI- und Persistenzschicht

Entity oder Value Object

MerkmalEntityValue Object
Identitäteigene, über die IDkeine, nur der Wert
Änderbarkeitveränderlichunveränderlich
Eigenständigkeitsteht für sichgehört immer zu einer Entity
Datenbankeigene Tabellekeine eigene Tabelle

Drei Arten von Gleichheit

ArtGleich, wenn …Gilt für
Referenzbeide auf dieselbe Stelle im Speicher zeigenbeide
Identifikatordie ID übereinstimmtEntity
Strukturalle Bestandteile übereinstimmenValue Object

Ein Value Object ändert man nicht, man erzeugt ein neues aus dem alten. Zwei frisch erzeugte Entities ohne ID sind nicht gleich — sie haben noch keine Identität.

Modul: Der erste Bounded Context

Aggregate zuschneiden

EigenschaftWas daraus folgt
Begriffliches GanzesDie Teile ergeben einzeln wenig Sinn
InvariantenDas Aggregat ist zu jedem Zeitpunkt gültig
Genau eine WurzelVon außen zeigt jeder Verweis auf sie
Einheit beim Laden und SpeichernAm Stück geholt, am Stück gesichert
KonsistenzgrenzeInnen sofort konsistent, darüber hinaus später

Vier Fragen an einen Zuschnitt

FrageAntwort und Folge
Ergibt die Entity ohne die andere Sinn?Ja: eigene Wurzel. Nein: Teil des Aggregats
Wie viele Entities stecken darin?In der Praxis ein bis drei, selten mehr
Wie groß wird die Sammlung?Ab etwa 30 Einträgen eigenes Aggregat daraus
Wie oft wird gleichzeitig geschrieben?Je größer das Aggregat, desto mehr Konflikte

Value Objects zählen bei der Größe nicht mit. Aggregate aus einer einzigen Entity sind völlig in Ordnung.

Modul: Aggregate im Bounded Context

Repositories

RegelBegründung
Eines je AggregatDie Wurzel ist die Einheit, die geladen und gesichert wird
Nach außen nur WurzelnInnere Entities bleiben hinter der Grenze
Innere Teile gehen mitNiemand von außen speichert ein Teil einzeln
Name ohne SpeichertechnikDer Aufrufer soll nicht wissen, woher die Daten kommen

Ein Repository darf am ORM vorbei direkt abfragen — es bleibt trotzdem der einzige Weg zur Datenbank. Was es zurückgibt, sagt der Rückgabetyp: entweder eine vollständige Entity mit haltenden Invarianten oder ehrliche Lesedaten für die Anzeige, aber nie eine halb gefüllte Entity.

Modul: Repositories

Persistenz ohne Verschmutzung des Kerns

Wo die Abbildung liegt

StrategieBewertung
Externe Konfigurationsdateiengeschwätzig, brechen beim Umbenennen unbemerkt
Annotationen an den Klassenknapp, aber Persistenzwissen wandert in den Kern
Eigene Mapping-KlassenKern bleibt frei, Umbenennen bleibt nachvollziehbar

Wer die ID vergibt

StrategieWie sie funktioniertPreis
Datenbank vergibtSchlüssel entsteht beim EinfügenRückfrage nötig, bricht das Bündeln
UUIDClient erzeugt den Schlüssel selbstsperrig zu lesen, breitere Indizes
Hi/LoClient reserviert einen Block auf Vorrateigene Tabelle für die Blocknummern

Die Wahl entscheidet, ob eine Entity schon vor dem Speichern identifizierbar ist — und damit, ob die Gleichheitsprüfung greift.

Was das ORM verlangt

ZugeständnisWarum das ORM es verlangt
Klassen nicht als final markierenStellvertreter erben von ihnen
Zugriffe nicht privat haltender Stellvertreter muss sie überschreiben
Parameterloser KonstruktorObjekte entstehen über Reflexion
Realen Typ statt Stellvertreter vergleichensonst ist eine Entity sich selbst ungleich

Dazu die zwei Bausteine, die jedes ORM kennt: die Session (verfolgt Objekte, schreibt gebündelt zurück — kurzlebig, eine je Interaktion) und die Session-Factory (hält Konfiguration und Verbindungen — eine je Datenbank).

Modul: UI- und Persistenzschicht

Bounded Context und Context Map

EigenschaftWas daraus folgt
Grenze der FachspracheDerselbe Name darf anderswo etwas anderes heißen
Eigene volle SchichtungJeder Kontext hat eigene Entities und Repositories
Ausgesprochene BeziehungenWas zwischen ihnen läuft, steht in der Context Map

Ein Bounded Context ist keine Schicht der Zwiebel — er hat eine eigene.

BegriffRaumWer ihn festlegt
SubdomäneProblem: ein Teil der AufgabeKunde und Fachexperten
Bounded ContextLösung: ein Teil des Entwurfsdas Entwicklungsteam

Beziehungen in der Karte

BeziehungBedeutung
Shared KernelBeide teilen ein Stück Modell und pflegen es gemeinsam
KonformistEiner fügt sich dem Modell des anderen
SchutzschichtEiner übersetzt, um Begriffe des anderen fernzuhalten
Getrennte WegeKeine Beziehung — die Kontexte wissen nichts voneinander

Grade physischer Trennung

GradWas getrennt istPreis
Ordner im selben Projektnur die NamensräumeGrenzen hält allein Disziplin
Eigene ModuleBauteile und Abhängigkeitenmehr Bauaufwand
Eigene AuslieferungProzesse, Datenbanken, QuellcodeNetz, Betrieb, Versionierung

Was zwischen Kontexten geteilt wird

CodeartTeilen?
Code mit FachlogikNur wenn beide dasselbe darunter verstehen
Basisklassen der DomäneInnerhalb eines Teams ja, über Teams hinweg nein
Werkzeug- und HilfscodeKlein: kopieren. Groß und wertvoll: eigenes Projekt

Modul: Der zweite Bounded Context

Domain Events

Ein Domain Event meldet eine fachliche Tatsache, keine Anweisung — deshalb Vergangenheitsform, nur einfache Datentypen, keine Entities oder Value Objects im Ereignis.

EreignisArtFachlich bedeutsam?
Knopfdruck auf dem BedienfeldSystemnein
Fenster geschlossen, Zeitgeber abgelaufenSystemnein
Waschgang gestartet und Karte belastetDomäneja

Erzeugen gehört der Entity, Zustellen der Infrastruktur: Die Wurzel sammelt ihre Ereignisse, zugestellt wird erst nach dem Speichern — vorher kann der Vorgang noch scheitern. Danach die Sammlung leeren. Der Behandler liegt beim Empfänger, nie beim Absender.

Lage der KontexteZustellung
Ein Prozessüber den Arbeitsspeicher, kein Transport nötig
Getrennte Prozesseüber einen Nachrichtenbus
Verlauf wird gebrauchtEreignisse werden gespeichert und später gelesen

Modul: Domain Events

Domain Service oder Anwendungsservice

MerkmalDomain ServiceAnwendungsservice
Lageinnerhalb der Domänenschichtaußerhalb
Trägt Fachwissenjanein
Spricht mit der Außenweltneinja
BeispielKarten auf Automaten verteilenEingaben prüfen, Aufruf anstoßen

Beide sind zustandslos. Ein Domain Service ist fällig, wenn der Vorgang zur Domäne gehört, aber zu keiner einzelnen Entity — ein Fahrzeug wartet sich nicht selbst. Wer nur weiterreicht und nichts fachlich entscheidet, ist keiner.

Immer gültig oder später prüfen

FrageImmer gültigSpäter prüfen
Wo wird geprüftim Konstruktor und in jeder Änderungin einer eigenen Prüfmethode
Was man bekommtJedes Objekt in der Hand ist gültigalle Prüfungen an einer Stelle
Was es kostetPrüfungen verteilen sich im ModellMan muss ans Prüfen denken

Eine verletzte Invariante ist ein Fehler im Programm, kein Eingabefehler.

Modul: Ausblick und Anti-Patterns

Anti-Patterns am Code erkennen

Zeichen im CodeDeutet auf
Alle Felder öffentlich, Logik in ServicesAnämisches Modell
Entity mit fachfremder AufgabeFette Entity
Entity fragt selbst die DatenbankFette Entity
Invarianten lassen sich von außen brechenbeides

Modul: Ausblick und Anti-Patterns

Typische Fallen

  • DDD über eine CRUD-Maske legen — Aufwand ohne Gegenwert; ebenso der Einsatz gegen Performance- oder Datenmengenprobleme.
  • Mit der Datenbank anfangen statt mit dem Modell, oder das Schema zuerst entwerfen und das Modell daran anpassen.
  • Persistenzwissen im Kern — Tabellennamen in der Entity, Spaltennamen als Zeichenketten, oder der Kern, der selbst ein Repository aufruft.
  • Fachlogik im Anwendungsservice, weil es dort schneller geht. Der Kern bleibt dann eine leere Hülle.
  • Innere Entities nach außen reichen — über einen Getter oder über das Repository. Beides macht die Aggregatgrenze löchrig.
  • Teile eines Aggregats einzeln speichern und es zerrissen zurücklassen.
  • Ein Repository je Entity statt je Aggregat, oder Methoden ergänzen, die noch niemand braucht: Löschen, Suchen, Zählen.
  • Ein Value Object mit ID-Feld ausstatten, weil das ORM es angeblich braucht — oder Felder aus dem Vergleich auslassen.
  • Oberflächenereignisse zu Domain Events erklären und das Modell fluten; ebenso Ereignisse in Befehlsform benennen („Saldo erhöhen” statt „Saldo geändert”).
  • Vor dem Speichern zustellen und bei einem Abbruch nichts zurücknehmen können; oder die Sammlung danach nicht leeren.
  • Den geteilten Kern wachsen lassen, bis er die Kontextgrenze faktisch aufhebt. Die Context Map dazu nie wieder anfassen.
  • Muster anwenden, ohne die Frage benennen zu können, die sie lösen — oder zu jedem Begriff das volle Satzmuster anlegen, ohne Bedarf zu prüfen.

Zum Seminar Domain-Driven Design in der Praxis