Start / Seminare / Domain-Driven Design in der Praxis
Modul
Der erste Bounded Context
7 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
DDD — Der erste Bounded Context
0:00 Im ersten Modul haben wir viel darüber geredet, wann DDD sich lohnt. Jetzt wird gebaut — und zwar in einer Reihenfolge, die den meisten Entwicklern zunächst gegen den Strich geht. Wir fangen nicht bei der Datenbank an und auch nicht bei der Oberfläche, sondern bei einem Modell, das rechnet, bevor es irgendetwas davon gibt.
0:18 Am Ende dieses Moduls haben Sie zwei Basisklassen, die Sie im ganzen Seminar begleiten, einen Kartenautomaten, der Geld annimmt und zurückgibt, und eine Antwort auf die Frage, die in DDD-Projekten am häufigsten falsch beantwortet wird: Ist das hier eine Entity oder ein Value Object?
Der erste Bounded Context
0:34 Sieben Kapitel, ein Ziel: ein lauffähiges Domänenmodell des Kartenautomaten. Der Weg dorthin führt bewusst über einen Umweg — wir bauen den ersten Entwurf absichtlich naiv und schauen dann, was er uns verrät. Danach kommt die Unterscheidung zwischen Entity und Value Object, die beiden Basisklassen, und zum Schluss der testgetriebene Aufbau von Geldbetrag und Automat.
0:56 Alles, was in den folgenden Modulen passiert — Persistenz, Repositories, Aggregate, Events — setzt auf genau diesen beiden Klassen auf.
Begriffe und Aufgabenstellung
1:05 Bevor wir modellieren, brauchen wir zwei Dinge: eine klare Aufgabenstellung und eine geklärte Sprache. Das ist keine Formalie — genau diese beiden Punkte entscheiden darüber, ob wir am Ende über dasselbe reden. Beides klingt nach Vorarbeit und ist in Wahrheit der Teil, an dem später die meisten Missverständnisse hängen. Nehmen Sie sich für diese beiden Folien Zeit.
1:26 Der erste Bounded Context des Waschsalons ist der Kartenautomat. Er nimmt Bargeld an, verrechnet den laufenden Vorgang und lädt am Ende Guthaben auf eine Karte. Das ist der ganze Umfang. Achten Sie darauf, wie eng dieser Schnitt ist: Waschautomat und Filialverwaltung existieren in der Domäne selbstverständlich weiter, aber sie kommen in diesem Modell nicht vor.
1:47 Diese Enge ist kein Mangel, sondern die Definition eines Bounded Context — er ist der Bereich, in dem ein Modell gilt. Was außerhalb liegt, bekommt später sein eigenes Modell, auch wenn es dieselben Wörter benutzt. Diese Tabelle ist unser eigener kleiner Sprachvertrag für das Seminar, und sie folgt damit genau der Regel aus Modul 1.
2:08 Domäne und Problemdomäne meinen dasselbe: das Problem, das die Software löst. Kerndomäne ist der Teil davon, den niemand zuliefern kann — der Grund, warum Sie überhaupt selbst entwickeln. Fachlogik und Domänenmodell sind das kodierte Wissen darüber. Wichtig ist die letzte Zeile mit ihrer Fußnote: Eine Domänenklasse lebt in den beiden inneren Ringen.
2:28 Ein Anwendungsservice ist ausdrücklich keine — er koordiniert Abläufe, aber er trifft keine fachlichen Entscheidungen. Drei Vorgänge, und die dritte Zeile ist die interessante. Geld annehmen, einzeln eingeworfen — so funktioniert ein realer Münzeinwurf, und deshalb funktioniert unser Modell auch so. Abbrechen und zurückgeben ist der Fall, den man in Anforderungsdokumenten gern vergisst und der in der Realität ständig vorkommt.
2:54 Und beim Aufladen passiert etwas, das man leicht überliest: Das Eingeworfene wandert in den Bestand des Automaten, das Rückgeld kommt aus demselben Bestand wieder heraus. Zwei Töpfe, eine Bewegung. Alles andere — Karten, Programme, Buchungen — lassen wir bewusst weg. Der Reiz liegt darin, wie viel Fachlogik schon in diesen drei Vorgängen steckt.
3:16 Der erste Punkt ist der Klassiker schlechthin: mit der Datenbank anfangen. Das fühlt sich solide an, legt aber das Modell auf ein Tabellenschema fest, bevor irgendjemand die Fachregeln kennt. Der zweite ist der spezifisch DDD- Anfängerfehler — man hat von Bounded Contexts gehört und schneidet sofort fünf davon, obwohl der ganze Code in eine Datei passt.
3:37 Grenzen kosten Aufwand, und der lohnt sich erst, wenn es etwas zu trennen gibt. Punkt drei ist die Erfindungsfalle: Vorgänge modellieren, die der Fachbereich nie erwähnt hat. Und der vierte Punkt ist eine Vorwarnung auf das, was gleich kommt.
Der erste Wurf des Kartenautomaten
3:52 Jetzt bauen wir den Automaten — und zwar erst einmal so, wie man ihn ohne DDD- Wissen bauen würde. Das ist kein pädagogischer Trick, sondern die realistische Ausgangslage. Der Reiz liegt darin, was dieser Entwurf über sich selbst verrät. Denn so, wie er dasteht, funktioniert er einwandfrei — und trägt trotzdem einen deutlichen Hinweis in sich, den man lesen können muss.
4:14 Der naive Entwurf hält für jede Münz- und Scheinsorte ein eigenes Feld — sechs Sorten für den Bestand im Automaten, dieselben sechs noch einmal für den laufenden Vorgang. Und jetzt kommt der Satz, auf den es ankommt: Er funktioniert. Genau deshalb lohnt der zweite Blick. Refactoring-Diskussionen scheitern oft daran, dass sie mit „das ist falsch" beginnen — hier ist nichts falsch, es ist nur unnötig mühsam.
4:37 Der Entwurf trägt einen Hinweis in sich, und diesen Hinweis lesen zu können, ist eine der praktisch wertvollsten Fähigkeiten im Modellieren. Schauen Sie nicht auf die einzelnen Felder, schauen Sie auf das Muster. Da stehen zwei Gruppen, die exakt gleich aufgebaut sind: einmal mit dem Zusatz „Innen" für den Bestand, einmal mit dem Zusatz „Vorgang".
4:59 Sechs Sorten, doppelt geführt — und jede Methode, die damit rechnet, muss alle sechs anfassen. Wenn morgen ein Zwanzig-Euro-Schein dazukommt, ändern Sie an einem Dutzend Stellen. Zwei gleich aufgebaute Feldgruppen sind kein Zufall, sondern ein Hinweis: Da gibt es ein Konzept, das noch keinen Namen hat. Der Code sagt es Ihnen, man muss nur hinhören.
5:20 Der entscheidende Schritt ist der erste, und er ist kein technischer: Geben Sie dem wiederholten Muster einen Namen. Hier heißt es Geldbetrag — ein Begriff, den der Fachbereich versteht. Danach ist die Arbeit fast mechanisch: Felder und Rechenoperationen wandern in die neue Klasse, im Automaten bleiben zwei Felder übrig, und die Methoden sprechen nur noch über den neuen Typ statt über sechs Einzelzahlen.
5:43 Wichtig ist der fünfte Schritt, den man gern überspringt: Prüfen, ob der Automat dadurch tatsächlich kürzer und verständlicher geworden ist. Wird er es nicht, war es die falsche Abstraktion — dann zurückrollen, nicht schönreden. Vergleichen Sie das mit dem, was vorher dastand. Zwei Felder statt zwölf, und die Methoden lesen sich plötzlich wie Sätze: Einwerfen addiert Geld zum laufenden Vorgang, Zurückgeben setzt ihn auf leer.
6:08 Der Automat rechnet nicht mehr selbst — er delegiert das an den Geldbetrag und kümmert sich nur noch um seine eigene Aufgabe. Achten Sie außerdem auf das Plus: Es liefert einen neuen Geldbetrag zurück, statt den alten zu verändern. Das ist kein Stilzitat, sondern ein Vorgriff auf die nächste Frage. Dieselbe Fachlogik, deutlich weniger Code — und das ist kein Zufall, sondern die Regel bei einer richtig gewählten Abstraktion.
Entity oder Value Object
6:33 Wir haben jetzt zwei Klassen, die auf den ersten Blick gleich aussehen: einen Kartenautomaten und einen Geldbetrag. Beide sind Klassen mit Feldern und Methoden. Fachlich könnten sie unterschiedlicher kaum sein — und dieser Unterschied entscheidet über fast alles Weitere. Und diese Unterscheidung ist keine akademische Feinheit: Aus ihr folgen Unveränderlichkeit, Gleichheitsregeln und am Ende sogar das Datenbankschema.
6:57 Der Automat ist eine Entity, der Geldbetrag ein Value Object. Der Unterschied lässt sich an einer Alltagsfrage festmachen: Woran erkennen Sie etwas wieder? Ihr Auto erkennen Sie an der Fahrgestellnummer wieder — auch nach einer Neulackierung ist es dasselbe Auto. Einen Zwanzig-Euro-Schein erkennen Sie an gar nichts wieder, und es interessiert Sie auch nicht: Zwanzig Euro sind zwanzig Euro. Genau das ist die Trennlinie.
7:22 Die Entity hat eine eigene Identität, die über ihren Zustand hinaus bestehen bleibt. Das Value Object zählt allein über seinen Wert. Diese Tabelle klärt eine Verwechslung, die in Diskussionen viel Zeit kostet. Referenzgleichheit ist die technische Frage, ob zwei Variablen auf dieselbe Stelle im Speicher zeigen — die gibt es immer, für beide Bausteine, und sie ist fachlich uninteressant.
7:46 Interessant sind die anderen zwei. Bei einer Entity entscheidet der Identifikator: gleiche ID, gleiche Entity, egal wie unterschiedlich der Zustand ist. Beim Value Object entscheidet die Struktur: Stimmen alle Bestandteile überein, ist es dasselbe. Die Fußzeile zieht daraus die Konsequenz — Value Objects haben kein ID-Feld, weil sie es gar nicht sinnvoll füllen könnten.
8:09 Diese vier Zeilen hängen alle an derselben Ursache, und das ist der eigentliche Lerneffekt: Sie müssen sich nicht vier Regeln merken, sondern eine. Weil ein Value Object keine Identität hat, kann es sich auch nicht ändern — es gäbe ja nichts, was über die Änderung hinweg gleich bliebe. Weil es keine Identität hat, kann es nicht für sich stehen und braucht eine Entity, zu der es gehört. Und weil es keine Identität hat, braucht es auch keine eigene Tabelle.
8:35 Die Fußzeile bringt es auf den Punkt: Zwei Zwei-Euro-Stücke sind austauschbar, zwei Automaten sind es nie. Das klingt nach Begriffsklauberei, hat aber sehr konkrete Folgen. Ein Value Object ändern Sie nicht — Sie erzeugen ein neues aus dem alten, so wie Sie beim Rechnen aus fünf und drei eine Acht machen, statt die Fünf zu verändern.
8:55 Eine Entity dagegen behält ihre Identität, während sich ihr Zustand über Jahre verschiebt; der Automat von heute ist derselbe wie der von letztem Jahr, obwohl kein einziger Wert mehr stimmt. Und der letzte Punkt zeigt, warum sich das lohnt: Aus dieser Einordnung folgt direkt das Datenbankschema, mit dem wir uns im nächsten Modul beschäftigen.
Value Objects erkennen
9:14 In der Theorie ist die Unterscheidung klar. In der Praxis sitzen Sie vor einem Begriff wie „Adresse" und wissen es nicht. Deshalb jetzt ein paar Fragen, die in den meisten Fällen weiterhelfen — und eine Beobachtung, die viele überrascht. Denn was in der Theorie sauber getrennt ist, sieht im Projekt oft nach beidem gleichzeitig aus — und dann hilft nur ein Satz brauchbarer Prüffragen.
9:37 Hier steckt die wichtigste Einsicht dieses Kapitels: Ob ein Begriff Entity oder Value Object ist, entscheidet die Domäne — nicht der Begriff. Es gibt keine Liste, auf der Geld für immer als Value Object steht. Im Waschsalon ist Geld ein Value Object, weil ein Zwei-Euro-Stück wie jedes andere ist. In einer Software, die einzelne Banknoten mit Seriennummer verfolgt, wäre exakt dieselbe Sache eine Entity, denn dort ist jeder Schein unterscheidbar und soll es sein.
10:04 Wer nach der allgemeingültigen Antwort sucht, sucht das Falsche. Die Frage lautet immer: Wie ist es in dieser Domäne? Fünf Fragen, und die Fußzeile sagt Ihnen schon, dass die ersten beiden meistens reichen. Die erste ist der Austauschtest: Können Sie eine Instanz gegen eine gleich aufgebaute tauschen, ohne dass es auffällt? Die zweite ist die Zahlenfrage — ist jede Fünf dieselbe Fünf?
10:28 Die dritte fragt nach der Herkunft: Interessiert fachlich, woher diese Instanz stammt? Die vierte prüft, ob es überhaupt eine Entity gibt, zu der der Begriff gehören kann. Und die fünfte ist eine Faustregel für den Zweifelsfall: Nehmen Sie Value Object. Ein Value Object später zur Entity zu machen, ist deutlich einfacher als der umgekehrte Weg.
10:50 Warum diese Voreinstellung? Weil Value Objects fast nur Vorteile haben. Sie sind unveränderlich, also können Sie sie gefahrlos herumreichen, ohne dass jemand sie Ihnen unter den Händen ändert — und Sie können sie trivial testen. Sie sind leichtgewichtig: kein ID-Feld, keine Tabelle, kein Lebenszyklus, um den man sich kümmern müsste.
11:09 Und der dritte Punkt ist der wichtigste für die Modellqualität: Fachlogik liegt am besten in ihnen, während die Entities schlanke Hüllen darum bleiben. Der letzte Punkt ist der Praxistest — die Adresse. Sie wird fast immer als Entity angelegt, und sie ist fast nie eine. Jetzt üben Sie das an Ihrer eigenen Domäne. Nehmen Sie fünf Begriffe und ordnen Sie jeden begründet ein — Entity oder Value Object.
11:34 Entscheidend ist die Begründung: Ein Satz je Begriff, der auf die Domäne verweist, nicht auf die Technik. „Braucht eine Tabelle" ist keine Begründung, sondern eine Folge. Und nehmen Sie bewusst zwei Grenzfälle dazu, etwa Adresse oder Zeitraum. Genau bei denen werden Sie merken, dass die Antwort davon abhängt, was Ihr Fachbereich mit dem Begriff eigentlich anstellt — und das ist die Erkenntnis, um die es hier geht.
Die Entity-Basisklasse
11:59 Wenn alle Entities dieselben zwei Eigenschaften teilen — eine Identität und eine Gleichheitsregel —, dann sollte man das genau einmal schreiben. Wie diese Basisklasse aussieht und warum sie eine Klasse ist und keine Schnittstelle, klären wir jetzt. Sie ist die erste von drei Basisklassen, die wir in diesem Seminar bauen, und zugleich die, über deren Bauform am meisten gestritten wird.
12:21 Zwei Dinge teilen sich alle Entities: ein Identitätsmerkmal und die Regel, wann zwei von ihnen als gleich gelten. Beides ist reine Wiederholung — es gibt keinen Grund, das in jeder Klasse neu zu schreiben. Also gehört es in eine abstrakte Basisklasse. Das Wort „abstrakt" ist dabei kein Zufall: Es gibt keine Entity an sich, die man instanziieren könnte.
12:42 Und dann kommt der Zusatz, über den in Teams gern gestritten wird — nicht in eine Schnittstelle. Warum das so ist, hat vier gute Gründe, und die stehen auf der nächsten Folie. Der erste Grund ist rein praktisch: Eine Schnittstelle trägt keine Logik. Jede Entity müsste die ID selbst anlegen und die Gleichheit selbst ausformulieren — das ist exakt die Verdopplung, die wir vermeiden wollten.
13:06 Der zweite Grund ist der interessantere, weil er semantisch ist. Eine Schnittstelle drückt aus, dass etwas eine bestimmte Fähigkeit hat: kann fliegen, kann gespeichert werden. Hier wollen wir aber nicht sagen „kann etwas", sondern „ist eine Entity" — eine Wesensaussage, und dafür ist Vererbung das richtige Mittel. Der letzte Punkt warnt vor einer verbreiteten Fehlannahme: Zwei Klassen mit derselben Schnittstelle sind fachlich noch lange nicht verwandt.
13:32 Der Kern dieser Klasse sind vier Prüfungen, und jede fängt einen realen Fehler ab. Ist das andere Objekt überhaupt eine Entity? Ist es vom selben Typ — denn eine Karte mit der ID sieben und ein Automat mit der ID sieben haben miteinander nichts zu tun. Dann die dritte Prüfung, die überrascht: Ist eine der beiden IDs noch null, gelten die Objekte als ungleich.
13:53 Frisch erzeugte Entities haben eben noch keine Identität; sie für gleich zu erklären, wäre schlicht falsch. Erst danach wird verglichen. Wer die ID überhaupt setzt, klärt das nächste Modul — der Kern tut es jedenfalls nicht. Die ersten beiden Punkte sind genau die Fälle, die unsere Prüfungen abfangen — und sie treten in der Praxis ständig auf, wenn man sie nicht behandelt.
14:15 Besonders die frisch erzeugten Entities ohne ID: Ein Test legt zwei neue Objekte an, vergleicht sie, und plötzlich sind sie gleich, obwohl sie es nicht sind. Der dritte Punkt ist die Wartungsfalle — die Gleichheitsregel je Entity neu schreiben statt sie zu erben, mit dem üblichen Ergebnis, dass die zwölfte Klasse sie leicht anders implementiert.
14:34 Und der vierte gilt für alle Sprachen mit Hash-basierten Sammlungen: Wer die Gleichheit ändert, muss auch den Hashwert mitziehen, sonst verschwinden Objekte in Mengen und Maps.
Die Value-Object-Basisklasse
14:44 Für Value Objects brauchen wir dasselbe — nur ist es hier kniffliger. Denn strukturelle Gleichheit lässt sich, anders als der ID-Vergleich, nicht ein für alle Mal formulieren. Die Lösung ist eine Arbeitsteilung zwischen Basisklasse und Unterklasse. Denn was bei Entities in zwei Zeilen erledigt war, geht hier nicht — und die Lösung ist eine Arbeitsteilung, die man sich merken sollte.
15:07 Warum geht es hier nicht so einfach? Weil strukturelle Gleichheit von den Bestandteilen der jeweiligen Klasse abhängt. Ein Geldbetrag vergleicht seine Stückzahlen, eine Adresse ihre Straße und Postleitzahl — das kann eine Basisklasse nicht wissen. Also teilen wir die Arbeit auf: Die Basisklasse übernimmt die Vorprüfungen, die immer gleich sind, und erzwingt, dass jede Unterklasse den eigentlichen Kern selbst liefert.
15:31 Das Wort „erzwingt" ist dabei entscheidend. Wir hoffen nicht darauf, dass jemand daran denkt — wir machen es unmöglich, es zu vergessen. Die Aufteilung sehen Sie hier in zwei Hälften. Oben die abstrakte Methode für den Kernvergleich — sie hat keinen Rumpf, also muss jede Unterklasse sie liefern, sonst übersetzt der Code nicht. Das ist die erzwungene Hälfte.
15:53 Unten die Vorprüfungen: Ist das andere Objekt überhaupt vorhanden? Ist es vom exakt gleichen Typ? Erst wenn beides stimmt, wird an die Unterklasse übergeben. Der Typvergleich ist dabei bewusst streng — eine Unterklasse ist eben nicht dasselbe wie die Oberklasse, auch wenn die Werte übereinstimmen. Der eigentliche Gewinn liegt in der Fußzeile: Vergessen fällt beim Übersetzen auf, nicht im Betrieb.
16:18 Der Nutzen lässt sich in einem Satz zusammenfassen: weniger Wiederholung, mehr Sicherheit. Null-Prüfung und Typprüfung stehen einmal da statt in jedem Value Object, und jede Unterklasse beschreibt nur noch, welche Bestandteile fachlich zählen. Der zweite Punkt ist der wertvollste — die abstrakte Methode erzwingt den Vergleich, statt ihn zu erhoffen.
16:38 Und der letzte Punkt ist eine Mahnung, die Sie sich merken sollten: Alle Bestandteile gehören dazu. Auch die, die man selten vergleicht, auch die, die man für unwichtig hält. Sonst gelten zwei Objekte als gleich, die es fachlich nicht sind — und dieser Fehler ist später sehr schwer zu finden. Genau davor warnt auch der erste Punkt: Einzelne Felder aus dem Vergleich auslassen und damit Gleichheit vortäuschen.
17:02 Der zweite ist ein Vorgriff auf das nächste Modul und einer der häufigsten Sündenfälle überhaupt — ein Value Object bekommt ein ID-Feld, weil das ORM es angeblich braucht. Es braucht es nicht, und wir werden sehen, wie es ohne geht. Der dritte Punkt beschreibt den Denkfehler, die Basisklasse zu klug machen zu wollen: Sie kennt die Bestandteile nicht, also kann sie den Vergleich auch nicht fest verdrahten.
17:24 Und der vierte ist der Rückfall in alte Gewohnheiten — ein Value Object verändern, statt ein neues zu erzeugen.
Geldbetrag und Automat mit Tests
17:31 Jetzt kommt alles zusammen. Wir bauen den Geldbetrag als Value Object und den Automaten als Entity — testgetrieben, ohne Oberfläche und ohne Datenbank. Und Sie werden sehen, wie viel Fachlichkeit in diesen wenigen Zeilen steckt. Und Sie werden sehen, wie viel Fachlichkeit in diesen wenigen Zeilen steckt — deutlich mehr, als die schlichte Aufgabe eines Münzeinwurfs vermuten lässt.
17:54 Eine Modellierungsentscheidung, die den Unterschied macht: Der Geldbetrag zählt Stücke je Sorte, nicht einen Eurobetrag. Das klingt umständlich, ist aber fachlich zwingend. Zehn Zehn-Cent-Stücke sind für einen Automaten, der Rückgeld auszahlen muss, etwas völlig anderes als ein Euro — mit dem einen Euro kann er nichts anfangen, wenn im Fach nur Zehner liegen.
18:15 Wer hier eine simple Dezimalzahl nimmt, verliert diese Information unwiederbringlich. Dazu kommt eine Invariante: Negative Stückzahlen darf es nie geben. Und das prüft nicht irgendein Service, sondern der Konstruktor — ein Geldbetrag entsteht gar nicht erst in einem ungültigen Zustand. Drei Dinge sind hier wichtig. Erstens die Konstante für den leeren Betrag: ein einziges Objekt, das überall wiederverwendet wird — bei unveränderlichen Objekten ist das gefahrlos und spart eine Menge Erzeugungen.
18:45 Zweitens die Prüfung im Konstruktor: Sie ist die Invariante des Value Objects und garantiert, dass es keinen Geldbetrag mit negativen Stückzahlen gibt, in keinem Zustand des Programms. Und drittens die Plus-Methode: Sie liefert einen neuen Geldbetrag zurück, statt den bestehenden zu ändern — die Unveränderlichkeit aus dem vorigen Kapitel, jetzt in Code.
19:05 Die Sortenreihenfolge steht übrigens fest, von zehn Cent bis zehn Euro. Hier sehen Sie eine Fachregel als Invariante der Entity: Es lässt sich immer nur genau ein Stück einwerfen. Technisch könnte man beliebige Beträge übergeben — der Automat lehnt es ab. Achten Sie auf die Fußzeile, denn sie enthält die eigentliche Lektion: Dass nur ein Stück hineinpasst, hat der Fachbereich entschieden, nicht der Code.
19:28 Es ist eine Aussage über den realen Münzeinwurf, kein technisches Detail. Genau so sollten Fachregeln entstehen — und genau deshalb steht sie an dieser Stelle im Kern und nicht in irgendeiner Eingabevalidierung der Oberfläche. Beachten Sie die Reihenfolge, die im Untertitel steht: rot, dann grün. Zuerst schreiben Sie diesen Test, er schlägt fehl, und erst dann bauen Sie die Rückgabe.
19:51 Der Test selbst ist wieder in drei Blöcke gegliedert — Automat anlegen und einen Euro einwerfen, dann zurückgeben, dann prüfen, dass der laufende Vorgang leer ist. Interessant ist, was der Test nicht braucht: keinen Mock, keine Datenbank, keine Konfiguration. Er läuft in Millisekunden, weil der Kern von beidem unabhängig ist.
20:11 Das ist die Belohnung für die Isolation, über die wir im ersten Modul gesprochen haben — hier zahlt sie sich zum ersten Mal aus. Der erste Punkt ist der Rückfall ins veränderliche Denken: eine Leeren-Methode am Value Object, die den Betrag auf null setzt. Richtig wäre, eine neue Instanz zu nehmen. Der zweite Punkt ist der wichtigste beim testgetriebenen Arbeiten — grüne Tests, die auch ohne die Implementierung grün wären.
20:36 Deshalb muss der Test vorher nachweislich fehlschlagen; das ist keine Zeremonie, sondern der einzige Beleg dafür, dass er überhaupt etwas prüft. Punkt drei erinnert an Modul 1: Randfälle entscheidet der Fachbereich, nicht der Entwickler. Und Punkt vier beschreibt die Schwerkraft, gegen die Sie ständig arbeiten — Fachlogik rutscht gern in die Entity, obwohl sie ins Value Object gehört.
20:59 Jetzt zum praktischen Teil. Sie formulieren eine Invariante eines Value Objects zuerst als Test und erfüllen sie dann — zum Beispiel die Regel, dass sich nie mehr abziehen lässt, als der Betrag enthält. Das Erfolgskriterium ist bewusst zweiteilig: Der Test muss vor der Implementierung nachweislich fehlschlagen und danach grün sein, ohne dass ein anderer Test bricht.
21:20 Der zweite Teil ist der unterschätzte. Wenn eine neue Fachregel bestehende Tests bricht, haben Sie entweder die Regel falsch verstanden oder etwas Interessantes über Ihr Modell gelernt — beides lohnt das Nachfragen.
Was du mitnimmst
21:32 Drei Dinge, die weit über dieses Modul hinaustragen. Erstens: Wiederholte Feldgruppen sind der Hinweis auf eine fehlende Abstraktion — der Code sagt Ihnen, was ihm fehlt, wenn Sie das Muster erkennen. Zweitens die Kernfrage: Entity heißt Identität, Value Object heißt Wert, und daraus folgt alles Weitere bis hin zum Datenbankschema.
21:51 Drittens die Faustregel für die Verteilung der Fachlogik — sie gehört ins Value Object, die Entity bleibt die Hülle darum. Und damit sind wir bei der Frage, die Modul 3 stellt: Wie kommt dieses Modell eigentlich in eine Datenbank, ohne dass es dabei Schaden nimmt?
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →