Domain-driven Design (DDD) ist einer der wichtigsten Ansätze, um fachliche Funktionalitäten in einem System zu organisieren und abzubilden. Konzepte wie Strategic Design und Bounded Context sind ausgesprochen hilfreich, um grobgranulare Module wie Microservices zu schneiden. Die Konzepte sind im praktischen Einsatz oft komplizierter als gedacht.
Der Begriff “Bounded Context” sagt klar, dass es um einen begrenzten Bereich geht. Ein Bounded Context begrenzt unterschiedliche Dinge:
- Der Bounded Context definiert den Einsatzbereich eines Domänenmodells. Es umfasst die Geschäftslogik für eine bestimmte Fachlichkeit. Als Beispiel beschreibt ein Domänenmodell die Buchung von S-Bahn-Fahrkarten und ein weiteres die Suche nach S-Bahn-Verbindungen. Da die beiden Fachlichkeiten wenig miteinander zu tun haben, sind es zwei getrennte Modelle. Für die Fahrkarten sind die Tarife relevant und für die Verbindung die Zeit, das Fahrziel und der Startpunkt der Reise.
- Außerdem soll der Bounded Context den Gültigkeitsbereich einer sogenannten Ubiquitous Language festlegen. Die Bezeichnung lässt sich mit allgegenwärtige Sprache übersetzen und beschreibt eine Menge von Begriffen, die Domänenexperten verwenden und die für Softwareartefakte wie Code oder Datenbank-Schemata genutzt werden sollen. Beispielsweise erschließt sich die Bedeutung des Begriffs “Taktverstärker” nicht sofort, den die Münchener Verkehrsbetriebe für zusätzliche S-Bahn-Züge zu den Stoßzeiten verwenden. Ein System für die Münchner S-Bahn sollte daher einen Taktverstärker als Klassennamen im Code und als Name einer Tabelle im Datenbank-Schema verwenden. Das Beispiel zeigt, dass die Ubiquitous Language ohne Wissen über den fachlichen Kontext nicht zu verstehen ist.
- Ein Bounded Context sollte in den Zuständigkeitsbereichs genau eines Teams fallen. Ein Team kann für mehr als ein Bounded Context zuständig sein, aber umgekehrt sollten nicht mehrere Teams an einem Bounded Context arbeiten, da das Vorgehen zu viel Abstimmung erfordert.
Somit ist ein Bounded Context ein Gültigkeitsbereich eines Domänenmodells, einer Ubiquitous Language und die Basis für die Organisation des Projekts.
In der Praxis
Als Beispiel für ein System nach dem DDD-Gedanken soll eine Bücherei dienen. Zwei wesentliche Funktionalitäten einer Bibliothek sind das Suchen und das Ausleihen von Büchern. Sie unterscheiden sich stark genug, dass zwei unterschiedliche Bounded Contexts sinnvoll erscheinen. Für die Funktionalitäten müssen jeweils Geschäftslogik und Daten über Domänenobjekte wie Bücher oder Leser bereitstehen (s. Abb. 1).
Interessant ist die Entität “Buch” in den beiden Kontexten: Im Bounded Context “Ausleihe” ist ein “Buch” ein konkretes Exemplar, das man in die Hand nehmen kann. Für eine ISBN existieren beliebig viele solcher Bücher.
Für die Suche ist ein “Buch” hingegen eine Sammlung von Daten wie Autor, Titel oder Schlagwörter. Ein solches Buch kann mehrere ISBNs haben, da das gedruckte Buch und das E-Book desselben Titels unterschiedliche ISBNs erhalten. Das Buch in der Suche und in der Leihe sind somit vollständig unterschiedliche Konzepte, die andere Daten haben und eine andere Identität, wie das Verhältnis zur ISBN belegt.
Das Buch hat in der Leihe und in der Suche zudem jeweils einen unterschiedlichen Lebenszyklus: Ein zusätzliches Exemplar erfordert im Kontext der Leihe einen Vermerk, aber in der Suche ändert sich nichts, denn der Sucheintrag existiert ab oder sogar vor dem ersten Exemplar. Bei der Aufnahme eines Buchs in die Suche, muss es in der Leihe noch nicht verfügbar sein. Das Anlegen eines neuen Buchs ist daher nur beim Betrachten in einem bestimmten Bounded Context sinnvoll.
Die inhaltliche Trennung eröffnet die Chance auf mehrere kleine, spezialisierte Domänenmodelle statt übermäßig komplexer großer Domänenmodelle. Das hilft nicht nur bei objektorientierten Modellen, sondern lässt sich ebenso für Datenbankmodelle anwenden, die häufig ebenso viel zu komplex sind.
Nicht nur für die Entwicklung, sondern auch für die Analyse der Domäne ergeben sich Vorteile: Der Begriff “Buch” ist in der Bibliothek offensichtlich nicht eindeutig definierbar. Durch die Bounded Contexts lässt sich in der Analyse damit umgehen, dass “Buch” in unterschiedlichen Bereichen anders definiert sein kann. Innerhalb eines Bounded Context gibt eine eindeutige Definition von “Buch”, die aber nicht darüber hinaus gilt [1].
Wenn mehrere Domänenmodelle Informationen über das Buch enthalten, wirkt das wie redundante Datenhaltung, aber wegen der unterschiedlichen Fachlichkeiten haben die Domänenmodelle tatsächlich kaum redundante Daten. Informationen wie die Anzahl der Exemplare gehören eindeutig zur Leihe, Information wie die Schlagwörter hingegen eindeutig zur Suche. Es kann sicher Informationen geben, die in beiden Bounded Context relevant sind, aber weil die beiden Bounded Contexts unterschiedliche Domänenmodelle haben und sogar unterschiedliche Ubiquitous Languages gelten, sind solche redundanten Informationen die Ausnahme.
Spezialisierte, kleine Domänenmodelle sind ein wesentliches Konzept von Domain-driven Design. In der Praxis ist es allerdings manchmal nicht ganz einfach, dieses Konzept umzusetzen. Der Ausgangspunkt für den Entwurf sind die Funktionalitäten der Bounded Contexts. Sie müssen die dafür benötigten Daten und die Logik umfassen. Das entspricht objektorientiertem Design, bei dem die Umgangsformen und Methoden einer Klasse im Mittelpunkt stehen und erst in der Folge die Daten eine Rolle spielen.
Viele Entwürfe gehen jedoch von den Daten aus. Das führt für die beispielhafte Bibliothek zu einem Problem, weil es in beiden Bounded Contexts “Leihe” und “Suche” das “Buch” gibt, es aber in jedem Bounded Context etwas anderes bedeutet. Eine Modellierung nach den Daten führt kaum zu einer Aufteilung in sinnvolle Bounded Contexts, sondern eher zu übermäßig komplexen Modellen. Wichtig ist, die Daten als Folge der Funktionalitäten zu modellieren.
Keine grünen Wiesen mehr
Es gibt kaum Projekte, bei denen Systeme auf der grünen Wiese entstehen. Meistens gibt es andere IT-Systeme, die es zu integrieren oder abzulösen gilt. Beispielsweise könnte in der Bibliothek ein Buchsystem existieren, das alle Informationen zu Büchern umfasst und das in einem Widerspruch zu der erläuterten, idealen Modellierung steht:
- Das Buchsystem hat ein Modell mit allen Daten eines Buchs. Wie erwähnt ist eine Aufteilung in mehrere Modelle mit spezialisierten Modellierungen des Buchs beispielsweise für Suche oder Leihe wesentlich sinnvoller.
- Ein System, das lediglich Daten verwalten soll, implementiert keine Funktionalitäten, die Grundlage für die Aufteilung nach Bounded Context ist. Systeme, die nur Daten verwalten, bilden die wesentliche Domänenlogik nicht ab.
Das Buchsystem erfüllt dennoch ein Merkmal eines Bounded Context: Es hat ein eigenes Domänenmodell. Es verwaltet alle Informationen über Bücher.
Ein anderes Merkmal eines Bounded Context erfüllt das Buchsystem nicht: Es setzt keine Ubiquitous Language um. Weil das System alle Informationen zu einem Buch verwaltet, setzt es sie weder für die Leihe noch die für die Suche um. Das Datenmodell hat vermutlich eine Klasse “Buch”, aber das Datenmodell muss die Daten für Leihe, Suche und gegebenenfalls einige weitere Funktionalitäten implementieren. Daher kann die Klasse “Buch” im Buchsystem keiner der beiden Ubiquitous Languages entsprechen und somit keinem der Begriffe der Domänenexperten.
Es gibt also Domänenmodelle, die keine Ubiquitous Language abbilden. Im Beispiel müsste ein solches Modell alle Informationen für Bücher verwalten. Dabei geht es nicht nur um die Anzahl der Attribute, sondern auch darum, die Konzepte “Buch” in Leihe und Suche abzubilden. Da sie grundverschieden sind, entsteht ein kompliziertes und wegen der Mehrdeutigkeiten verwirrendes Modell. Zudem ändert sich bei jeder fachlichen Änderungen an der Leihe oder der Suche der Bücherservice, wenn sich etwas an der Modellierung der Bücher ändert. Auf die Weise kann er zu einem Änderungsschwerpunkt werden, den es bei jeder fachlichen Änderungen anzupassen gilt.
Die spezialisierten Modelle sind auf jeden Fall kleiner, einfacher und weniger verwirrend. Sie versprechen zudem, dass Änderungen mit einer höheren Wahrscheinlichkeit nur ein Domänenmodell umfassen. Eines der zentralen Versprechen von Bounded Contexts ist, dass mehrere kleinere Domänenmodelle oft ein besserer Weg sind, um mit der Komplexität umzugehen.
Beim Umgang mit dem Buchsystem sollten die Verantwortlichen somit eine Ablösung erwägen, um die Vorteile von Domain-driven Design vollständig umzusetzen. Das ist jedoch eventuell aufwendig, da das Domänenmodell in dem Buchsystem komplex ist. Außerdem kann das System Abhängigkeiten zu vielen anderen Systemen haben. Schließlich kommt in einer Bibliothek wohl kaum ein IT-System ohne Informationen über Bücher aus, die sich das jeweilige System vom Buchsystem holt.
Wegen der Komplexität der Domänenmodelle kann es dennoch eine gute Idee sein, von solchen Systemen weg zu migrieren oder sie zumindest nicht weiter wachsen zu lassen. Dazu lässt sich ein neues System mit einem Bounded Context wie “Leihe” oder “Suche” implementieren, das zunächst die benötigten Daten aus dem Buchsystem repliziert. Das Buchsystem bleibt zunächst für die Daten führend.
Wenn der neue Bounded Context später für die Informationen führend ist, lässt sich die Richtung der Replikation umkehren. Dann erfolgt beispielsweise die Verwaltung der Information über die Anzahl der vorhandenen Exemplare im Bounded Context Leihe und das Buchsystem hat nur eine Kopie der Informationen. Gegebenenfalls ist die Anzahl der Exemplare nur für die Leihe relevant. In dem Fall lässt sich auf das Replizieren der Daten verzichten. Weil die benötigten Daten eine Folge der im Bounded Context implementierten Funktionalitäten sind, ist es sogar recht wahrscheinlich, dass nur ein Bounded Context die Information benötigt.
Wenn es dennoch andere Systeme gibt, die an der Anzahl der Exemplare interessiert sind, wenden sie sich nicht mehr an das Buchsystem, sondern den Bounded Context “Leihe”. Nach der Umsetzung verschwinden diese Informationen aus dem Buchsystem, dessen Komplexität dadurch sinkt. Nach und nach lässt sich das Buchsystem eliminieren.
Dabei handelt es sich um einen langwierigen Prozess, da das Ablösen eines solchen Abhängigkeitsschwerpunkts nur mit erheblichen Aufwand realisierbar ist. Weit vor der endgültigen Ablösung ergibt sich der Vorteil, dass die Leihe nun ein eigenes kleines Domänenmodell hat, das wesentlich einfacher zu verstehen und weiterzuentwickeln ist. Das kann sofort zu einem Produktivitätsvorteil führen, um neue Anforderungen in dem Bounded Context Leihe umzusetzen. Es kann durchaus sein, dass der Aufwand für eine vollständige Migration keine ausreichende Produktivitätssteigerung zur Folge hat und es daher nie zur vollständigen Ablösung des Buchsystems kommt.
Strategic Design
Bisher standen nur einzelne Bounded Contexts im Fokus, aber ein Bounded Context kann nicht in Isolation existieren. Wenn Leser nach einem Buch suchen, sollte sich idealerweise ein Leihvorgang anschließen. Dazu ist eine Schnittstelle notwendig, mit der die Suche der Leihe Informationen übergeben kann, sodass Büchereibesucher das richtige Buch ausleihen können.
Domain-driven Design behandelt Beziehungen zwischen Bounded Contexts im Strategic Design. In diesem Bereich existieren unterschiedliche Patterns: Bei Customer/Supplier kann das Team, das für “Leihe” verantwortlich ist, dem Team, das für “Suche” verantwortlich ist, Anforderungen geben, die letzteres Team umsetzen muss.
Beim Open-Host-Service würde hingegen die “Suche” anderen Systemen eine generische Schnittstelle anbieten, die auch die “Leihe” nutzen kann. Open-Host-Service hält dadurch die Anzahl der Schnittstellen gering. Ein Open-Host-Service hat organisatorische Auswirkung: Wer den Service verantwortet, nimmt zwar Anforderungen anderer Teams entgegen, aber am Ende entscheidet das Team, ob es die Anforderungen umsetzt oder die Anforderungen in der generischen Schnittstelle nicht berücksichtigt, weil sie zu speziell sind. Das letzte Wort liegt dazu bei dem Team, das den Open-Host-Service verantwortet.
Strategic Design scheint Beziehungen zwischen Teams festzulegen. In Wirklichkeit handelt es sich jedoch um Beziehungen zwischen Kontexten: Das Team, das einen Bounded Context verantwortet, übernimmt die damit verbundenen Rechte und Pflichten. Das für einen Bounded Context verantwortliche Team muss nicht konstant sein, da die Änderungsschwerpunkte über den Verlauf eines Projekts nicht gleich bleiben. Um dennoch die Teams gleichmäßig auszulasten, müssen sie Bounded Contexts tauschen.
Beispielsweise kann anfangs je ein Team das Buchsystem, die Leihe und die Suche verantworten. Das Buchsystem ist ein Open-Host-Service, sodass das “Suche”-Team und das “Leihe”-Team Anforderungen stellen können, die das “Buchsystem”-Team umsetzt, wenn sie in den Plan des Teams passen. Letzteres Team könnte nun die Verantwortung für das “Buchsystem” an das “Leihe”-Team übergeben. Das Buchsystem bleibt dabei ein Open-Host-Service. Das “Leihe”-Team muss somit die Anforderungen von “Suche” an das “Buchsystem” entgegennehmen, bewerten und gegebenenfalls im “Buchsystem” umsetzen, weil es die Pflichten mit der Übernahme des “Buchsystems” übernommen hat.
Kartografierte Verflechtungen
Die Beziehungen zwischen den Bounded Contexts wie in den Abbildungen 2 und 3 stellen eine Context Map dar. die oft zur Darstellung des Sollzustand eines Systems dienen. In einem IT-System oder einer IT-Landschaft gibt es jedoch bereits Domänenmodelle, zwischen denen Beziehungen existieren. Eine Context Map lässt sich zur Analyse des Ist-Zustands nutzen. Damit sind Bounded Contexts wie ein Buchsystem die Regel: Sie haben zwar ein Domänenmodell, aber entsprechen keiner Ubiquitous Language, weil sie ohne Rücksicht auf die Erkenntnisse von DDD entstanden sind. Bei einem Sollmodell entsprechen mehr Bounded Contexts dem DDD-Ideal, wenn eine Migration in Richtung DDD geplant ist.
Bei den Beziehungen zwischen den Bounded Contexts können andere Patterns auftreten als im Domain-driven Design vorgesehen. Beispielsweise kann das Buchsystem zwar eine generische API anbieten, aber das “Buchsystem”-Team hat nicht das letzte Wort, welche Änderungen tatsächlich in die API fließen. Ein Grund dafür könnte sein, dass ein anderes Team politisch mächtiger ist und seine Anforderungen auf jeden Fall durchsetzen kann. Die Abhängigkeiten und Schnittstellen auf der Software-Ebene sind dann genau so wie bei einem Open Host Service, aber die Beziehungen auf Team-Ebene unterscheiden sich.
Obwohl die Unterschiede als Detail erscheinen mögen, haben sie potenziell erhebliche Konsequenzen. Wenn das Team nicht die Möglichkeit hat, die Entwicklung der API zu steuern, kann es passieren, dass die API irgendwann schwer zu verstehen und zu benutzen ist, weil sie keinem eindeutigen Konzept mehr folgt. Weil Abweichungen von den DDD-Pattern zu Herausforderungen führen können, ist es wichtig, die Beziehungen präzise zu dokumentieren und Abweichungen gegebenenfalls zu untersuchen. Es kommt durchaus vor, dass Abweichungen sinnvoll sind oder nicht zu Problemen führen. In dem Fall ist es nicht unbedingt notwendig, die Beziehungen zu ändern.
Drei Ebenen
Auf drei unterschiedlichen Ebenen existieren Bounded Contexts: dem Datenmodell, der Ubiquitous Language und den Teams.
Bounded Contexts haben spezialisierte Datenmodelle. In jedem der Datenmodelle kann es gleich benannte Domänenobjekte wie “Buch” geben, die aber meistens keine redundanten Daten enthalten, sondern unterschiedliche fachliche Aspekte eines solchen Domänenobjekts modellieren. Die Aufteilung in mehrere kleine Datenmodelle ist ein wesentlicher Grund, warum DDD-Entwürfe einfacher sein können. Gleichzeitig ist es oft eine Hürde zu akzeptieren, dass unterschiedliche Datenmodelle Informationen über ein Domänenobjekt wie ein Buch haben.
Die Ubiquitous Language stellt eine Sammlung von Begriffen dar, die Domänenexperten nutzen und sich in der Software wiederfinden sollten. Für die Leihe und die Suche ist das Buch etwas anderes. Mit einem Bounded Context können mehrdeutige Begriffe eindeutig werden. Ein Buchsystem durchbricht dieses Design, weil es alle Informationen zu dem Buch sammelt und beide Ubiquitous Languages umfassen muss. Es zeigt somit, dass ein Bounded Context mit einem Domänenmodell nicht unbedingt ebenfalls einer für eine Ubiquitous Language sein muss.
Ein Bounded Context definiert die Rechte und Pflichte des Teams, das dafür verantwortlich ist. Wenn ein anderes Team ihn übernimmt, erhält es damit auch die Rechte und Pflichten.
Weil ein Bounded Context also verschiedene Aspekte umfasst, erscheint es sinnvoll, Bounded Contexts für Domänenmodelle und Bounded Contexts für die Ubiquitous Languages zu unterscheiden – auch wenn Domain-driven Design das nicht direkt vorsieht.
Schließlich ist es wichtig, die Aufteilung in Bounded Contexts zu analysieren und den Ist-Zustand klar von einem möglichen Sollzustand abzugrenzen. In bereits vorhandenen Legacy-Systemen existieren auf jeden Fall Bounded Contexts für Domänenmodelle, aber solche für Ubiquitous Languages finden sich gegebenenfalls nicht wieder, weil Systeme Daten aus mehreren Ubiquitous Languages verwalten. Gerade diese Erkenntnisse sind interessant, um Systeme zu verbessern und weiterzuentwickeln.
Der Autor dankt seinen Kollegen Martin Kühl, Tammo van Lessen, Martin Otten, Joachim Praetorius und Johannes Seitz für die Kommentare zu einer früheren Version des Artikels.
Literatur
-
Eric Evans: Domain–driven Design Referenz, kostenlos ↩