Start / Seminare / Domain-Driven Design in der Praxis
Modul
Der zweite Bounded Context
6 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 zweite Bounded Context
0:00 Bisher hatten wir ein Modell, und alles darin war widerspruchsfrei. Jetzt kommt ein zweites dazu — und damit die Frage, die in großen Systemen über alles entscheidet: Was passiert an der Grenze zwischen zwei Modellen? Der Waschsalon bekommt seinen Waschautomaten, und der ist ausdrücklich kein weiteres Kapitel im bestehenden Modell, sondern ein eigener Bounded Context.
0:21 Wir schauen uns an, warum das keine Umständlichkeit ist, sondern eine Entwurfsentscheidung, wie man solche Grenzen findet, wie man festhält, was zwischen ihnen läuft — und wann man Code teilen darf. Die Antwort auf die letzte Frage ist strenger, als man erwartet.
Der zweite Bounded Context
0:36 Sechs Kapitel, und sie folgen einem Bogen vom Konkreten ins Strategische. Erst der Waschautomat als zweites Modell. Dann die Theorie: Was ist ein Bounded Context, wie verhält er sich zur Subdomäne, und wie hält man die Beziehungen in einer Context Map fest. Danach zwei sehr praktische Fragen, die in Architekturrunden regelmäßig durcheinandergehen — wie weit man Kontexte physisch trennt und welchen Code man zwischen ihnen teilen darf.
1:01 Ab hier zählt nicht mehr nur, was in einem Modell steht, sondern was zwischen zweien läuft.
Die neue Aufgabe: der Waschautomat
1:06 Fangen wir mit der neuen Anforderung an. Der Waschsalon braucht seinen Waschautomaten — und die erste Entscheidung fällt, bevor eine Zeile Code entsteht: Bauen wir ihn ins bestehende Modell oder daneben? Diese Entscheidung fällt in Projekten meist beiläufig — und bestimmt trotzdem, wie teuer jede Änderung an einem der beiden Geräte künftig wird.
1:26 Der Waschautomat startet ein Waschprogramm und belastet dafür die Guthabenkarte des Kunden — mit dem Programmpreis zuzüglich einem Prozent Abwicklungsentgelt. Und er führt selbst mit, was er insgesamt belastet hat. Klingt nach einer überschaubaren Erweiterung. Achten Sie aber auf die Begriffe: Programm, Abwicklungsentgelt, Belastung.
1:46 Kein einziger davon kommt im Kartenautomaten vor. Umgekehrt weiß der Waschautomat nichts von Fächern, Kartenstapeln und Rückgeld. Zwei Geräte, zwei Sprachen, zwei Regelwerke — und genau das ist das Kennzeichen zweier Bounded Contexts, nicht zweier Klassen im selben Modell. Die Überschrift ist wörtlich zu nehmen: Das ist alles. Ein Programm wird freigegeben, wenn die Belastung durchgeht. Belastet wird der Preis plus ein Prozent.
2:13 Die Summe bleibt nachvollziehbar. Und der vierte Punkt zieht wieder eine Grenze, die wir im ganzen Seminar durchhalten: Wie die Karte technisch gelesen wird, bleibt außen vor. Das ist Infrastruktur, kein Fachwissen. Diese Enge ist Absicht — je genauer Sie beschreiben können, was ein Kontext leistet und was nicht, desto klarer verläuft später seine Grenze.
2:34 Ein Kontext, dessen Aufgabe sich nicht in vier Sätzen sagen lässt, ist meist zu groß geschnitten. Das Interessante an diesem Code ist die Mischung. Der Geldbetrag stammt aus dem Shared Kernel — dieselbe Klasse, die schon der Kartenautomat benutzt, denn Geld ist in beiden Kontexten exakt dasselbe. Das Programm dagegen gibt es nur hier; der Kartenautomat kennt den Begriff nicht einmal.
2:57 Sie sehen also an einer einzigen Klasse, wo geteilt wird und wo nicht — und das ist keine Bauchentscheidung, sondern folgt einer Regel, die wir im letzten Kapitel dieses Moduls sauber herleiten. Fachlich ist die Methode unspektakulär: Entgelt rechnen, letzte Belastung merken, Summe fortschreiben. Der erste Punkt ist die ehrliche Versuchung: den Waschautomaten ins bestehende Modell hängen, weil es schneller geht. Kurzfristig stimmt das sogar — der Preis kommt später.
3:24 Der zweite ist der schleichende: Begriffe des Kartenautomaten mitbenutzen, ohne zu prüfen, ob sie hier gelten. Genau so entstehen Klassen, die in zwei Kontexten leicht Verschiedenes bedeuten und niemandem mehr gehören. Der dritte Punkt schiebt Fachlogik in die Oberfläche — das eine Prozent gehört ins Modell, sonst gilt es nur für diesen einen Bildschirm.
3:44 Und der vierte bindet beide Kontexte über die Datenbank aneinander, was die schwerste Fessel von allen ist.
Was ein Bounded Context ist
3:51 Damit haben wir zwei Modelle im selben System. Jetzt die Theorie dahinter: Was genau ist ein Bounded Context, was folgt daraus praktisch — und warum ein einziges großes Modell für alles nie funktioniert? Denn ein einziges großes Modell für alles hat in der Praxis noch nie funktioniert, und die Gründe dafür sind erstaunlich gut beschreibbar.
4:11 Ein Bounded Context trennt Modelle voneinander und zieht die Grenze ausdrücklich. Das Wort „ausdrücklich" ist der Kern. Grenzen zwischen Modellen gibt es in jedem größeren System — nur sind sie meist unausgesprochen, und dann verlaufen sie quer durch Klassen, in denen zwei Bedeutungen stecken. Der zweite Satz nennt den Zweck: Es ist das zentrale Muster gegen den Aufwand, der entsteht, wenn ein einziges Modell für alles herhalten muss.
4:36 Und dieser Aufwand wächst nicht linear, sondern beschleunigt — je größer das Modell, desto mehr Beteiligte muss jede einzelne Änderung überzeugen. Die erste Zeile ist die befreiende: Derselbe Name darf anderswo etwas anderes heißen. Damit ist die Sprachregel aus Modul 1 präzisiert — die allgegenwärtige Sprache gilt innerhalb eines Kontexts, nicht im ganzen Unternehmen.
4:59 Die zweite Zeile ist die anspruchsvolle: Jeder Kontext hat eine eigene volle Schichtung, also eigene Entities, eigene Repositories, eigene Services. Ein Kontext ist kein Paket, sondern eine kleine Anwendung. Die dritte verlangt, dass die Beziehungen ausgesprochen werden. Und die Fußzeile räumt eine sehr verbreitete Verwechslung aus: Ein Bounded Context ist keine Schicht der Zwiebel — er hat eine eigene.
5:24 Der erste Punkt ist eine Erfahrung, die jeder aus großen Projekten kennt: Je größer das Modell, desto mehr Abstimmung kostet jede Änderung. Irgendwann sitzen acht Leute im Termin, weil jemand ein Feld umbenennen will. Der zweite Punkt ist der sprachliche Kern der Sache — ein Begriff, der überall gelten muss, wird überall ein wenig falsch.
5:43 Er wird so weit gedehnt, bis er in jeden Kontext irgendwie passt und in keinem mehr genau ist. Der dritte spricht die Erlaubnis aus, die vielen schwerfällt: Zwei Kontexte dürfen dieselbe Klasse führen und Verschiedenes damit meinen. Und der vierte ordnet ein — die Trennung ist zuerst logisch, die physische Frage kommt später.
6:02 Der erste Punkt ist ein Fehler, der aus guter Absicht entsteht: eine gemeinsame Fachsprache über alle Kontexte hinweg erzwingen wollen. Das klingt nach Ordnung und produziert Begriffe, die niemand mehr präzise benutzen kann. Der zweite ist die konkrete Folge davon — zwei Konzepte in eine Klasse zwingen, weil sie zufällig gleich heißen.
6:22 Der dritte ist der halbherzige Kontext ohne eigene Repositories und Services; der hängt trotz Grenze am alten Modell. Und der vierte ist der, an dem später die meiste Zeit verlorengeht: Beziehungen zwischen Kontexten nur mündlich klären. Genau deshalb gibt es die Context Map, zu der wir gleich kommen.
Subdomäne und Bounded Context
6:40 Zwei Begriffe, die ständig verwechselt werden — und die Unterscheidung ist einfacher, als ihr Ruf vermuten lässt. Es geht darum, ob wir über das Problem reden oder über unsere Lösung. Wer sie sauber auseinanderhält, kann in Diskussionen außerdem sagen, wer bei einer Streitfrage eigentlich das letzte Wort hat. Eine Subdomäne ist ein Teil der zu lösenden Aufgabe, ein Bounded Context ein Teil des Entwurfs.
7:04 Merken lässt sich das an einem Satz: Die Subdomäne existiert auch dann, wenn Sie nie eine Zeile Software schreiben. Der Waschsalon hat den Verkaufsbereich und den Waschbereich, unabhängig davon, ob das jemand programmiert. Der Bounded Context ist Ihre Antwort darauf. Im Idealfall steht beides eins zu eins zueinander — je Subdomäne genau ein Kontext.
7:25 Das ist keine Regel, sondern eine Zielmarke: Wo Sie davon abweichen, sollten Sie den Grund nennen können. Die rechte Spalte macht den Unterschied greifbarer als jede Definition: Wer legt es fest? Die Subdomäne legen Kunde und Fachexperten fest — Sie können darüber nicht entscheiden, Sie können sie nur erkennen. Den Bounded Context legt das Entwicklungsteam fest; das ist Ihre Entscheidung, und Sie tragen die Folgen.
7:50 Diese Zuordnung hilft im Gespräch enorm, weil sie klärt, wer bei einer Streitfrage das letzte Wort hat. Und die Fußzeile beschreibt den häufigsten Sonderfall der Praxis: Zwei Kontexte für eine Subdomäne kommen vor, meist als Notlösung um Altlasten herum — ein Bestandssystem, das man nicht anfassen kann. Der erste Schritt ist der wichtigste und der am wenigsten technische: den Fachexperten zuhören.
8:14 Teilsysteme benennen sich meist von selbst — man hört es daran, wie Leute reden, worüber sie getrennt sprechen, welche Aufgaben sie in einem Atemzug nennen. Dann je Subdomäne ein Kontext, und damit wäre es meist getan. Schritt drei und vier sind Ausnahmen mit sehr konkreten Schwellen: Wächst das Team über sechs bis acht Personen, oder passt der Code nicht mehr in einen Kopf, wird geteilt.
8:36 Und Schritt fünf ist die organisatorische Regel dahinter: Teamgrenzen an Kontextgrenzen legen, nie zwei Teams auf einen Kontext. Sie lösen aus einer fachlichen Beschreibung die Subdomänen heraus und ordnen ihnen Kontexte zu. Jede Subdomäne mit einem Satz aus Fachsicht — das ist die Disziplin dabei, denn der Reflex geht sofort zu Systemnamen und Modulschnitten.
8:58 Und für jede Abweichung von der Eins-zu-eins-Zuordnung steht der Grund daneben. Der Hinweis nennt einen erstaunlich verlässlichen Indikator, den Sie in jedem Workshop anwenden können: Achten Sie darauf, wo Ihr Gegenüber von „dem System" spricht und wo von zwei Dingen. Sprache verrät Grenzen zuverlässiger als jedes Architekturdiagramm.
Die Context Map
9:17 Wenn es mehrere Kontexte gibt, gibt es Beziehungen zwischen ihnen — ob man sie aufschreibt oder nicht. Die Context Map schreibt sie auf. Und sie hat eine Eigenschaft, die ungewöhnlich ist für ein Architekturdokument. Und sie hat eine Eigenschaft, die sie von fast allen anderen Diagrammen in Projekten unterscheidet — eine, die viele zunächst irritiert.
9:38 Eine Context Map zeigt die Bounded Contexts eines Systems und ihre Beziehungen. Und jetzt kommt die ungewöhnliche Eigenschaft: Sie hält den Ist- Zustand fest, nicht den gewünschten. Das ist der Unterschied zu fast allen anderen Diagrammen, die in Projekten kursieren. Eine Karte, die zeigt, wie es sein sollte, ist ein Plan — nützlich, aber etwas anderes.
9:58 Wer beides vermischt, verliert genau den Wert, den die Karte haben soll: Sie soll Ihnen sagen, wo eine Änderung wehtut, und das hängt an den Verbindungen, die tatsächlich existieren, nicht an den gewünschten. Drei Beziehungsarten, die Sie am häufigsten brauchen werden. Der Shared Kernel: Beide teilen ein Stück Modell und pflegen es gemeinsam — mit Betonung auf gemeinsam, denn eine einseitige Änderung bricht den anderen.
10:23 Getrennte Wege sind die entspannteste Beziehung: keine Beziehung, die Kontexte wissen nichts voneinander. Und die Schutzschicht ist die defensive Variante — einer übersetzt, um sich die Begriffe des anderen vom Leib zu halten, typischerweise gegenüber einem Altsystem. Bei uns steht es in der Fußzeile: geteilter Kern beim Geldbetrag, sonst getrennte Wege. Erstaunlich wenig Kopplung für zwei Geräte, die im selben Raum stehen.
10:49 Der zweite Punkt ist der Alltagsnutzen: Sie sehen, wo eine Änderung über eine Grenze hinweg Folgen hat. Das ist die Frage, die vor jedem größeren Umbau gestellt wird und meist nur aus dem Gedächtnis beantwortet werden kann. Der dritte Punkt beschreibt den Ernstfall: Verfällt ein Kontext — weil er veraltet, weil das Team weg ist —, ist die Karte der Ausgangspunkt für die Schutzschicht.
11:10 Und der vierte ist die Warnung, die den ganzen Wert trägt: Eine geschönte Karte verschweigt genau die Stellen, um die es geht. Die peinlichen Verbindungen sind die teuren. Die ersten beiden Punkte sind die Wunschdenken-Fehler und der wichtigste Grund, warum Context Maps oft wertlos sind. Man zeichnet den sauberen Zielzustand, lässt die unerfreuliche direkte Datenbankverbindung weg — und hält das Ergebnis für eine Bestandsaufnahme.
11:34 Der dritte Punkt ist die schleichende Erosion: Der geteilte Kern wächst, weil jedes Teilen einzeln vernünftig aussah, bis die Grenze faktisch aufgehoben ist. Deshalb gehört jedes Wachstum des Kerns begründet. Und der vierte ist der Verfall — die Karte einmal zeichnen und nie wieder anfassen. Eine Karte von vor drei Jahren ist gefährlicher als keine, weil man ihr glaubt.
Grade physischer Trennung
11:56 Jetzt zu einer Frage, die in Architekturrunden regelmäßig alles durcheinander bringt: Müssen getrennte Kontexte auch getrennt ausgeliefert werden? Die Antwort ist nein — und die Abstufungen dazwischen sind das eigentlich Interessante. Die Abstufungen dazwischen sind das eigentlich Interessante, denn zwischen einem Ordner und einer eigenen Auslieferung liegen Welten an Aufwand.
12:17 Ob zwei Kontexte in einem Ordner, in eigenen Modulen oder in eigenen Anwendungen liegen, ist unabhängig davon, dass sie getrennt sind. Das ist der Satz, den man in Microservice-Diskussionen am dringendsten braucht. Die Trennung ist zuerst eine Frage des Modells, nicht der Verpackung. Und dann der ehrliche Zusatz: Mehr Trennung hält die Grenze besser — und kostet mehr.
12:38 Beides stimmt gleichzeitig, und genau deshalb gibt es keine allgemeingültige Antwort, sondern eine Abwägung, die Sie für Ihr Projekt treffen müssen. Die rechte Spalte ist wieder die ehrliche. Ordner im selben Projekt trennen nur die Namensräume — die Grenze hält allein Disziplin, und Disziplin hält nicht ewig. Eigene Module trennen Bauteile und Abhängigkeiten; jetzt merkt der Übersetzer, wenn jemand über die Grenze greift, und der Preis ist mehr Bauaufwand.
13:06 Eigene Auslieferung trennt Prozesse, Datenbanken und Quellcode — und kostet Netz, Betrieb und Versionierung, also die ganze verteilte Komplexität. Die Fußzeile ist die Empfehlung, die ich unterschreiben würde: klein anfangen und erst trennen, wenn die Disziplin nicht mehr reicht. Diese Tabelle verbindet die Trennungsgrade mit der Kommunikation, und Sie sehen die Steigerung von oben nach unten: aus einem Methodenaufruf wird ein Aufruf über den Übersetzer und schließlich einer über das Netz.
13:34 Dieselbe Steigerung gilt für Ereignisse — vom Domain Event im Prozess bis zur Nachricht über eine Warteschlange. Interessant ist die Fußzeile, weil sie einen Nebeneffekt benennt, den man nutzen kann: Bei getrennten Prozessen wirkt die Übertragung selbst schon als Schutzschicht. Was über das Netz geht, muss ohnehin übersetzt werden — die fremden Begriffe kommen gar nicht erst ungefiltert an.
13:56 Der erste Punkt ist der teuerste Fehler unserer Zeit: getrennte Auslieferung wählen, bevor der Code die Trennung überhaupt braucht. Sie zahlen die volle Rechnung für verteilte Systeme, ohne den Nutzen zu haben. Der zweite ist das Gegenstück am anderen Ende — im selben Projekt die Ordnergrenze überschreiten, weil nichts sie erzwingt.
14:14 Der dritte ist die Kopplung über Daten: Beide Kontexte schreiben in dieselben Tabellen, und ab da ist jede Modelländerung eine Abstimmung. Und der vierte ist die Zeremonie — eine Schutzschicht, wo es gar keinen fremden Begriff zu übersetzen gibt. Dann bauen Sie eine Übersetzung von Deutsch nach Deutsch.
Code zwischen Kontexten teilen
14:32 Zum Schluss die Frage, die am meisten Widerspruch auslöst — weil sie einem Grundsatz widerspricht, den die meisten von uns verinnerlicht haben. Wiederverwendung ist hier nämlich kein Wert an sich. Denn jede geteilte Klasse ist eine Verbindung zwischen zwei Kontexten — und die verlangt Abstimmung, sobald sich etwas ändert.
14:50 Gemeinsamer Code verbindet, was getrennt sein soll. Dieser Satz sitzt quer zu allem, was man über Wiederverwendung gelernt hat — und er ist trotzdem richtig. Jede geteilte Klasse ist eine Verbindung zwischen zwei Kontexten, die bei jeder Änderung Abstimmung erzwingt. Ob geteilt werden darf, hängt von der Art des Codes ab, und bei Fachlogik an genau einer Bedingung: ob beide Kontexte wirklich dasselbe darunter verstehen.
15:15 Nicht, ob der Code gleich aussieht. Ob die Bedeutung gleich ist. Das ist ein fachliches Kriterium für eine Entscheidung, die gewöhnlich technisch getroffen wird. Drei Codearten, drei sehr unterschiedliche Antworten. Fachlogik nur teilen, wenn beide Seiten dasselbe darunter verstehen — dazu gleich mehr. Basisklassen der Domäne, also unser Entity und ValueObject: innerhalb eines Teams ja, über Teamgrenzen hinweg nein, weil die Abstimmungskosten steigen und niemand mehr zuständig ist.
15:44 Und Hilfscode nach Größe: Kleines kopiert man — ja, kopieren, das ist hier die empfohlene Lösung —, Großes und Wertvolles wird ein eigenes Projekt mit eigener Versionierung. Die Fußzeile bringt die Faustregel: ein Satz Basisklassen je Team, nicht je Kontext. Das Beispiel in der Mitte ist das beste dieses Moduls. Der Verkauf sieht ein Produkt anders als der Kundendienst — für den einen ist es Preis, Marge und Verfügbarkeit, für den anderen Seriennummer, Garantiezeit und Reparaturhistorie.
16:15 Dasselbe Regal, zwei Modelle. Wer daraus eine Klasse macht, bekommt eine, die beiden nur halb dient. Beim Geldbetrag ist es genau umgekehrt: Beide Automaten meinen exakt dasselbe — Stückzahlen je Sorte, dieselben Regeln, dieselben Invarianten. Und nur dann gehört die Klasse in den geteilten Kern. Die Prüffrage lautet also nie „heißt es gleich", sondern „meint es dasselbe".
16:39 Der erste Punkt ist der Kernfehler dieses Kapitels: Fachlogik zusammenlegen, weil sich der Code ähnelt, nicht weil der Begriff gleich ist. Ähnlicher Code ist ein schwacher Indikator — zwei Berechnungen können heute identisch aussehen und morgen aus guten Gründen auseinanderlaufen. Der zweite unterschätzt die Abstimmung über Teamgrenzen hinweg.
16:58 Der dritte ist die Überhöhung: jede Hilfsfunktion zum firmenweiten Projekt erheben, mit Freigabeprozess und Versionsplan für dreißig Zeilen Code. Und der vierte ist der gefährlichste, weil er so harmlos wirkt: den geteilten Kern lockerer behandeln als einen Bounded Context. Er ist einer — nur einer, der zwei Herren dient.
17:17 Sie entscheiden für drei Kandidaten begründet, ob Code geteilt, kopiert oder getrennt gehalten wird — jeweils mit der Codeart, und bei Fachlogik zusätzlich mit einer Aussage zur Bedeutung in beiden Kontexten. Beachten Sie den Hinweis, denn er enthält den lehrreichsten Fall: Nehmen Sie einen Kandidaten dazu, bei dem die Namen gleich sind und die Bedeutung nicht.
17:37 Das ist die Situation, in der die falsche Entscheidung am naheliegendsten ist — und die richtige verlangt, dass Sie beim Fachbereich nachfragen, statt in den Code zu schauen.
Was du mitnimmst
17:47 Vier Punkte. Ein Bounded Context ist die Grenze, innerhalb derer ein Begriff eindeutig ist — nicht mehr, aber auch nicht weniger. Subdomäne gehört zum Problem, Bounded Context zur Lösung, im Idealfall eins zu eins. Die Context Map hält den Ist-Zustand fest, sonst nützt sie nichts; eine schöne Karte ist eine wertlose Karte.
18:06 Und Fachlogik wird nur geteilt, wenn beide Seiten dasselbe darunter verstehen. Damit steht die Frage im Raum, wie zwei getrennte Kontexte eigentlich voneinander erfahren, ohne sich fest aneinanderzubinden. Die Antwort heißt Domain Events — und das ist das nächste Modul.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →