Softwarearchitektur verstehen und entscheiden

Aaron KreisÜber mich
Domänenarchitektur

Bounded Context

Abgegrenzter Geltungsbereich eines Fachmodells

Die Idee und ihr Nutzen

In der Schadenabteilung einer Versicherung arbeiten drei Teams mit dem Wort „Schaden“, meinen damit aber Verschiedenes. Für das Team, das Schadenmeldungen annimmt, ist ein Schaden eine Meldung mit Datum, Ort und Fotos; für das Team, das Schäden bezahlt, ist derselbe Vorfall ein Fall mit Deckungsprüfung und Zahlungen; für das Team, das Geld vom Verursacher zurückfordert, ist er eine Forderung. Statt im Programm ein einziges Datenobjekt „Schaden“ zu bauen, also ein Datenpaket mit den Feldern für alle drei Bedeutungen, bekommt jedes Team einen eigenen Bereich, in dem „Schaden“ genau eine Bedeutung hat. Meldet eine Kundin einen Schaden, erhält der Bereich des Teams, das Schäden bezahlt, nur Schadennummer, Vertragsnummer und Schadendatum und legt daraus einen eigenen Fall an. Ein solcher abgegrenzter Bereich heißt Bounded Context, auf Deutsch etwa „abgegrenzter Zusammenhang“. Der Name meint: Die Bedeutung „Meldung mit Datum, Ort und Fotos“ gilt nur im Bereich des Teams, das Meldungen annimmt, und nicht darüber hinaus.

Bausteine: Bereich Schadenmeldung: „Schaden“ = Meldung mit Datum, Ort, Fotos; Bereich Regulierung: „Schaden“ = Fall mit Deckung, Reserve, Zahlungen; Bereich Regress: „Schaden“ = Forderung gegen den Verursacher. Verbindungen: Bereich Schadenmeldung — Bereich Regulierung (übersetzt); Bereich Regulierung — Bereich Regress (übersetzt). Jedes Team hat einen eigenen Bereich, in dem „Schaden“ genau eine Bedeutung hat; zwischen den Bereichen wird übersetzt.

Wie funktioniert es?

Eine Kundin meldet einen Wasserschaden vom 3. Mai. Im Bereich Schadenmeldung ist der Schaden eine Meldung mit Datum, Ort, Beschreibung und Fotos. Geprüft und bezahlt wird der Schaden aber im Bereich Regulierung. An der Grenze zum Bereich Regulierung erzeugt der Bereich Schadenmeldung die Nachricht „Schaden gemeldet“ mit Schadennummer, Vertragsnummer und Schadendatum. Die Nachricht enthält nur die Angaben, die der Bereich Regulierung braucht. Die Fotos gehören nicht dazu. Aus der Nachricht legt der Bereich Regulierung einen eigenen Fall mit Deckungsprüfung, Reserve und Zahlungen an. Die Reserve ist das Geld, das die Versicherung für den Schaden zurücklegt. Braucht der Bereich Regulierung die Fotos, schlägt er sie über die Schadennummer im Bereich Schadenmeldung nach, statt sie in seinen Fall zu kopieren. Ein Handwerker im Nachbarhaus hat den Schaden verursacht. Deshalb übergibt der Bereich Regulierung den Fall an den Bereich Regress, der Geld beim Verursacher zurückfordert. Im Bereich Regress wird der Schaden zu einer Forderung an die Haftpflichtversicherung des Handwerkers. Ändert das zuständige Team später die Forderungsregeln im Bereich Regress, müssen die Bereiche Schadenmeldung und Regulierung nichts anpassen.

Jeder Bereich hat ein eigenes Fachmodell, also eigene Begriffe, Datenobjekte und Regeln. Innerhalb eines Bereichs hat jeder Begriff genau eine Bedeutung, und jeder der drei Bereiche hat sein eigenes Datenobjekt „Schaden“ mit eigenen Feldern. Im Bereich Regress sind das zum Beispiel der Verursacher, dessen Versicherung und die Höhe der Forderung. Für jeden Bereich ist ein Team zuständig: das Team Schadenmeldung, das Team Regulierung und das Team Regress. Jedes dieser Teams arbeitet mit eigenen Fachleuten zusammen, ändert seine Abläufe zu eigenen Zeitpunkten und entscheidet über die Bedeutung der Begriffe und Regeln in seinem Bereich. Fachleute und Entwickler eines Teams verwenden dieselben Wörter mit derselben Bedeutung, im Gespräch und im Programmcode. Der Bounded Context legt nur fest, wo eine Bedeutung gilt. Wie ein Bereich technisch umgesetzt wird, ist eine eigene, spätere Frage, die das zuständige Team entscheidet. Ein Bereich kann ein eigener Teil eines Programms sein, ein eigenes Programm oder ein Programm mit eigener Datenbank.

Eine Übersicht, die Context Map, hält fest, welche Bereiche es gibt und wie sie zusammenarbeiten: welcher Bereich Daten liefert, welcher sie übernimmt und wo übersetzt wird. In der Context Map ist auch vermerkt, welcher Bereich für welche Angabe maßgeblich ist. Für das Schadendatum ist das der Bereich Schadenmeldung, für die Zahlungen der Bereich Regulierung. Grenzen gehören dorthin, wo sich die Bedeutung eines Begriffs ändert, wo verschiedene Fachleute zuständig sind oder wo sich Abläufe unabhängig voneinander ändern. Zwischen Schadenmeldung und Regulierung wird aus „Schaden“ zum Beispiel statt einer Meldung ein Fall. Berichtigt das Team Schadenmeldung das Schadendatum, erfährt der Bereich Regulierung davon nicht von selbst. Wie eine Änderung von einem Bereich in den anderen gelangt, müssen die Teams ausdrücklich vereinbaren.

Bounded Context: drei Bereiche, drei Bedeutungen von „Schaden“

Beteiligte: Kundin: meldet einen Wasserschaden vom 3. Mai; Bereich Schadenmeldung: Schaden = Meldung mit Datum, Ort, Fotos; Bereich Regulierung: Schaden = Fall mit Deckung, Reserve, Zahlungen; Bereich Regress: Schaden = Forderung gegen den Verursacher; Haftpflichtversicherung des Handwerkers: soll den Schaden ersetzen. Ablauf: 1. Die Kundin meldet ihren Wasserschaden mit Datum und Fotos. Im Bereich Schadenmeldung ist der Schaden eine Meldung mit Datum, Ort, Beschreibung und Fotos. 2. An der Grenze zum Bereich Regulierung erzeugt der Bereich Schadenmeldung die Nachricht „Schaden gemeldet“ mit Schadennummer, Vertragsnummer und Schadendatum. Die Fotos gehören nicht dazu. 3. Der Bereich Regulierung legt aus der Nachricht einen eigenen Fall mit Deckungsprüfung, Reserve und Zahlungen an. 4. Der Bereich Regulierung schlägt die Fotos bei Bedarf über die Schadennummer im Bereich Schadenmeldung nach, statt sie zu kopieren. 5. Ein Handwerker im Nachbarhaus hat den Schaden verursacht. Der Bereich Regulierung übergibt den Fall an den Bereich Regress. 6. Im Bereich Regress wird der Schaden zu einer Forderung an die Haftpflichtversicherung des Handwerkers. Ändert das Team Regress seine Forderungsregeln, müssen die Bereiche Schadenmeldung und Regulierung nichts anpassen.

Wann ist es sinnvoll und wann nicht?

Ein Bounded Context lohnt sich

  • Dasselbe Wort bedeutet in verschiedenen Abteilungen Verschiedenes. Ohne Grenzen bekommt ein einziges Datenobjekt „Schaden“ die Felder aller drei Teams, obwohl jedes Team nur einen Teil davon braucht. Dann bleibt unklar, ob ein Feld wie „Status“ den Stand „gemeldet“ aus dem Bereich Schadenmeldung meint oder den Stand „bezahlt“ aus dem Bereich Regulierung. Mit eigenen Bereichen enthält der Fall im Bereich Regulierung nur Deckungsprüfung, Reserve und Zahlungen. Den Status „bezahlt“ gibt es nur im Bereich Regulierung, „gemeldet“ nur im Bereich Schadenmeldung.
  • Mehrere Teams arbeiten an einem großen System mit vielen Fachgebieten. Ohne Grenzen nutzen alle Teams dasselbe Datenobjekt „Schaden“. Will das Team Regress dort ein Feld für den Verursacher ergänzen, muss es die Änderung mit dem Team Schadenmeldung und dem Team Regulierung abstimmen. Nach einigen Jahren versteht niemand mehr das ganze Datenobjekt, und eine Änderung des Teams Regress kann Programmteile des Teams Regulierung unbemerkt stören. Mit eigenen Bereichen ergänzt das Team Regress das Feld nur in seinem eigenen Datenobjekt „Schaden“.
  • Die Entwickler sollen im Programmcode dieselben Wörter verwenden wie die Fachleute, im Bereich Regress etwa „Forderung“ und „Verursacher“. Ohne eigene Bereiche müssten die Entwickler die Wörter aller drei Teams auf ein gemeinsames Datenobjekt „Schaden“ abbilden, und dabei entstehen Missverständnisse.
  • Ein großes System soll in Programmteile oder eigenständige Dienste zerlegt werden, und es braucht eine fachliche Grundlage für die Frage, wo die Grenzen liegen. Ohne diese Grundlage entsteht leicht ein Dienst, der Teile der Bereiche Regulierung und Regress enthält, und das Team Regulierung und das Team Regress müssen beide an diesem Dienst ändern. Mit Bounded Contexts kann jeder Bereich ein eigener Programmteil oder Dienst werden, etwa ein Dienst für den Bereich Regress.

Ein Bounded Context ist nicht das Richtige

  • Die Anwendung ist klein, ein einziges Team arbeitet daran, und ihre Aufgabe ist klar. Ein Beispiel ist eine Anwendung, mit der nur das Team Regress seine Forderungen verwaltet. Dort hat „Schaden“ ohnehin nur eine Bedeutung, und kein anderes Team ändert dieselben Datenobjekte. Dann arbeitet das Team mit einem gemeinsamen Fachmodell für die ganze Anwendung. Code, der an den Grenzen zwischen Bereichen übersetzt, und Nachrichten, die zwischen Teams abgestimmt werden müssen, entfallen.
  • Das Fachgebiet ist noch kaum verstanden. Das ist oft am Anfang eines neuen Produkts so. Dann arbeiten die Entwickler des neuen Produkts zunächst mit einem gemeinsamen Fachmodell. Grenzen ziehen die Entwickler erst, wenn sichtbar ist, wo sich die Bedeutung eines Begriffs ändert. Zu früh gezogene Grenzen verlaufen oft falsch und müssten später mit hohem Aufwand verschoben werden.
  • Zwei Abteilungen verwenden einen Begriff tatsächlich gleich und arbeiten ständig mit denselben Daten. Dann arbeiten beide Abteilungen im selben Bereich mit einem gemeinsamen Fachmodell. Nachrichten, die dieselben Daten ständig zwischen zwei Bereichen hin und her tragen, entfallen.

Nachteile und typische Fehler

Informationen über denselben Vorfall liegen in mehreren Bereichen in unterschiedlicher Form vor. Für das Übersetzen an den Grenzen braucht es zusätzlichen Code, der gepflegt werden muss. Die Nachrichten an den Grenzen müssen zwischen den Teams abgestimmt werden. Ein Beispiel ist die Nachricht „Schaden gemeldet“. Außerdem muss jemand die Context Map pflegen.

Typische Fehler und woran man sie erkennt:

  • Falsch gezogene Grenze: Dinge, die eng zusammengehören, liegen in verschiedenen Bereichen. Die Deckungsprüfung liegt zum Beispiel im Bereich Schadenmeldung, die Zahlungen im Bereich Regulierung. Vor jeder Zahlung muss der Bereich Regulierung dann das Ergebnis der Deckungsprüfung im Bereich Schadenmeldung abfragen, und jede Änderung an den Deckungsregeln betrifft das Team Schadenmeldung und das Team Regulierung. Erkennbar ist das daran, dass fast jede fachliche Änderung gleichzeitige Anpassungen in zwei oder drei Bereichen verlangt.
  • Fehlende Context Map: Niemand weiß, welcher Bereich für welche Angabe maßgeblich ist. Erkennbar ist das an Rückfragen wie „Welches Schadendatum stimmt?“ und an Berichten, die je nach Bereich andere Zahlen liefern.
  • Ungeregelte Änderung: Das Schadendatum wird in der Meldung berichtigt, aber für diese Änderung gibt es keine Nachricht an den Bereich Regulierung. Der Fall im Bereich Regulierung behält das alte Datum. Erkennbar ist das an verschiedenen Schadendaten zur selben Schadennummer.
  • Geteiltes Datenobjekt über die Grenze: Ein Bereich übernimmt das Datenobjekt eines anderen unverändert oder liest direkt in dessen Datenbank. Damit hängt der übernehmende Bereich doch wieder von den Begriffen des anderen ab. Der Bereich Regulierung liest zum Beispiel das Schadendatum direkt aus der Datenbank des Bereichs Schadenmeldung. Benennt das Team Schadenmeldung dieses Feld um, findet der Bereich Regulierung das Schadendatum nicht mehr. Erkennbar ist das daran, dass eine Änderung im Bereich Schadenmeldung Fehler im Bereich Regulierung auslöst.

Weiterführend

Verwandte Ansätze

  • Domain Driven Design: die Entwurfsmethode, aus der der Begriff Bounded Context stammt.
  • Anti Corruption Layer: ein Übersetzungsteil, der einen Bereich vor den Begriffen eines fremden Systems schützt.
  • Microservices und Modular Monolith: zwei Bauweisen, in denen fachliche Bereiche als eigene Dienste oder als getrennte Teile eines Programms umgesetzt werden können. Ein Bereich muss nicht genau einem Dienst entsprechen.
  • Conway's Law: beschreibt, dass Programmgrenzen oft den Teamgrenzen folgen. Bereichsgrenzen und Teamzuschnitt beeinflussen sich deshalb gegenseitig.
  • Layered Architecture: gliedert ein System nach technischen Aufgaben wie Oberfläche, Fachlogik und Datenzugriff. Das ordnet den Programmcode, klärt aber nicht, was „Schaden“ oder „Status“ jeweils bedeutet.

Werkzeuge und Umsetzungen

  • Context Mapper: beschreibt Bereiche und ihre Beziehungen in einer eigenen Textsprache und erzeugt daraus Context Maps als Grafik.
  • Egon: Werkzeug für Domain Storytelling. Fachleute erzählen ihre Arbeitsabläufe, die als Bildgeschichte aufgezeichnet werden. Dabei wird sichtbar, wo sich die Bedeutung von Begriffen ändert und wo Bereichsgrenzen liegen könnten.
  • jMolecules: kennzeichnet Bausteine im Java-Code als Teil eines Bereichs, sodass Prüfwerkzeuge die Grenzen kontrollieren können.
  • Spring Modulith: prüft in Spring-Anwendungen, ob die Teile eines Programms nur über die dafür freigegebenen Schnittstellen aufeinander zugreifen.

Quellen