Felix Kammerlander
26.11.2023
Das fachliche Schneiden von Software ist durch Domain Driven Design (DDD) und Microservices populär und allgegenwärtig. Je nach Problemstellung und Kontext ist es bei der Wahl von Bounded Contexts manchmal sinnvoll, Fachlichkeit übergreifend bereitzustellen und nicht zu zerschneiden. Dieser Artikel beschäftigt sich mit der Frage, wann ein solches Vorgehen nützlich ist – und wann nicht.
Diese Frage ergibt sich für unsere fiktive Online-Plattform „Spotiflix“ für Filme und Musik. Nutzer:innen können auf dieser Plattform Filme und Musik kaufen und abspielen.
Spotiflix ist aus dem Zusammenschluss eines Film- und eines Musikanbieters entstanden. Zunächst hat eine neu eingeführte UI-Integration die beiden Welten aus Nutzersicht verheiratet. Abbildung 1 zeigt die Architektur zu diesem Zeitpunkt.
Die Domäne von Spotiflix beinhaltet verschiedene Subdomänen: Für uns relevant sind hier die Warenkörbe zum Kauf sowie das Abspielen von Filmen und Musik. Weitere Subdomänen wären etwa Dinge wie „Bezahlungen“ oder „Authentifikation und Authorisation“. Wir fassen das Abspielen von Musik und Filmen (und was noch dafür benötigt wird) sowie die Warenkörbe jeweils in einem Bounded Context für Musik- oder Film-Streaming zusammen. Alternativ können wir die beiden Warenkörbe auch jeweils in einen eigenen Bounded Context stecken. Abbildung 2 zeigt die beiden Ansätze.
Einige Begrifflichkeiten werden mehrfach vorkommen und können jeweils unterschiedlich verstanden werden. Etwa könnte der Begriff „Produkt“ in verschiedenen Bounded Contexts auftauchen, welcher jedoch im jeweiligen Context eine unterschiedliche Bedeutung hat. Das entspricht genau der Idee der ubiquitous language, der allgegenwärtigen Sprache von DDD. Bounded Contexts definieren die Grenzen dieser Sprache und der Modellierung der Fachdomäne.
Dennoch stellt sich hier die Frage, ob nicht die Warenkörbe oder die Medienplayer in einem Bounded Context zusammengefasst werden sollten. Ergeben sich vielleicht Synergien, wenn wir die Modellierung der beiden Warenkörbe zusammenlegen? Können wir die Komplexität unserer Domänen reduzieren? Und bietet es sich in diesem Falle an, einen solchen zusammengefassten Context als eigene Applikation zu realisieren, um Kosten zu sparen?
Abbildung 3 zeigt, wie die Definition der Bounded Contexts mit einem zusammengelegten Bounded Context “Warenkorb” aussieht. Abbildung 4 zeigt eine mögliche architekturelle Umsetzung dieser Idee.
Stellt sicher, dass ihr die Domänenmodelle gut verstanden habt, bevor ihr solch einen Schnitt in Erwägung zieht! Die fachliche Überschneidung von „Filmwarenkorb“ und „Musikwarenkorb“ sollte nicht nur sehr groß sein, die Unterschiede sollten zusätzlich überschaubar oder wenig relevant für die Modellierung oder die abzubildende Fachlogik sein. Große Überschneidungen allein bedeuten nicht zwangsläufig, dass es eine gute Idee ist, die Fachlichkeit in einem Modell abzubilden. Nicht nur die Menge der Überschneidungen und Unterschiede spielt eine Rolle, sondern auch deren Qualität. Abbildung 5 veranschaulicht diese Problematik.
Das ist in unserem Fall jedoch gegeben: Egal ob Film oder Musik – Nutzer:innen kaufen ein digitales Gut, immer in einfacher Menge und mit unbegrenztem Zugriff. Lieferungszeiten und Transport spielen keine Rolle. Bei beiden Produkten sind zudem ähnliche Attribute wichtig, z.B. Künstler (Musiker:innen oder Schauspieler:innen), Erscheinungsjahr und Laufzeit. Unterschiede, wie die nötige Altersfreigabe bei Filmen, mag es zwar geben. Diese haben aber nur wenig Einfluss auf unseren Bounded Context. Der Unterschied ist nicht besonders relevant.
Die Situation stellt sich deutlich anders dar, wenn man etwa einen Warenkorb für Filme mit einem Warenkorb für Lebensmittel verheiraten wollte. Lebensmittel kaufen wir in einer gewissen Menge, sie haben ein gewisses Haltbarkeitsdatum, sie sind ggf. nicht verfügbar und Lieferzeit sowie Versand spielen eine Rolle. Dafür gibt es keine Künstler, das Erscheinungsjahr interessiert höchstens bei Hochprozentigem und eine Laufzeit für Lebensmittel gibt es auch nicht.
Ein hinreichend mächtiges Modell könnte selbstverständlich auch Filme und Lebensmittel in ein und demselben Warenkorb darstellen. Wir schleppen aber dann stets ein sehr komplexes Modell mit uns herum, welches oft gar nicht benötigt wird.
Der Warenkorb beinhaltet auf einmal sehr viel kontextabhängige Logik und wird zu einer komplexeren Applikation, als das die jeweils getrennten Warenkörbe waren. Viele Anforderungen an den Warenkorb werden nun durch die Lebensmitteldomäne getrieben.
Bildlich gesprochen „kontaminiert“ diese (deutlich komplexere) Lebensmitteldomäne den querschnittlichen Warenkorb-Context.
Nur wer die in Frage kommenden Teile wirklich verstanden und ordentlich modelliert hat, kann beurteilen, ob die Überschneidungen wirklich groß und die Unterschiede tatsächlich klein genug sind.
Die Frage nach Überschneidungen und Unterschieden zwischen den Bounded Contexts und deren Qualität sind die zentralen Kriterien, um zu entscheiden, ob die Teile zu einem querschnittlichen Bounded Context zusammengefasst werden könnten. Bei einer positiven Einschätzung kann man sich nun damit auseinandersetzen, ob man einen solchen querschnittlichen Context einführen sollte.
Hierfür sind etwa Qualitätsziele ein Kriterium, welches oftmals die Wahl unseres Schnittes beeinflusst. Gibt es große Abweichungen der Qualitätsziele, so solltet ihr die Applikationen lieber nicht zusammenfassen. Die Verbesserung eines Qualitätsziels macht die Erreichung anderer schwieriger, da sie manchmal in Konflikt zueinanderstehen.
Betrachten wir etwa konkret in unserem Beispiel den Medienplayer von Spotiflix. Wenn “Filmemedienplayer” und “Musikmedienplayer” hinreichend groß fachlich übereinstimmen, sollten wir uns nun fragen, ob die beiden Streaming-Contexts ähnliche Qualitätsziele erreichen müssen. Hier wären deutliche Unterschiede denkbar. Etwa könnten wir vom Filmemedienplayer erwarten, stets ein scharfes 4K-Bild ohne Artefakte zu liefern. Beim Abspielen von Musik auf Mobilgeräten priorisieren wir vielleicht eher Datensparsamkeit und Geschwindigkeit. In diesem Falle sollten wir die Anwendungen also eher nicht zusammenlegen.
Die beiden Warenkörbe haben hingegen sehr ähnliche Qualitätsziele, wie etwa Sicherheit und Bedienbarkeit. Dagegen weichen die Qualitätsziele für die Medien-Player stark ab. Werden nun beide Contexts von einem Team betreut, steigt die kognitive Last für dieses Team. Es muss nun Architektur, Technologien und Patterns für beide Ziele bereitstellen und erlernen.
Dies könnte eine weitere Motivation sein, den Warenkorb in einen eigenen Bounded Context auszulagern, von Musik- oder Filmplayer zu trennen und von einem weiteren Team betreuen zu lassen. Die Teams, welche die Player von Film und Musik betreuen, können sich dann technisch und fachlich deutlich mehr auf die Herausforderung ihres Bounded Contexts fokussieren, während das andere Team mit anderen Lösungen und Konzepten die Qualitätsziele des Warenkorbs verfolgt.
Bedenken sollte man außerdem, wie „stabil“ der querschnittliche Bounded Context ist. Bei Änderungen an den Schnittstellen müssen jedes Mal die anderen Teams mit an Bord geholt werden und man erhöht die Chance, dass man doch wieder den Context „kontaminiert“. Der querschnittliche Bounded Context ist vor allem dann gut geeignet, wenn die fachlichen Anforderungen selten Abhängigkeiten zu den anderen Bereichen aufweisen.
Wenn wir nur einen statt zwei Warenkörben bauen, sollten wir schließlich zudem Kosten sparen können. Allerdings ist das nicht immer der Fall. Welche Dinge ihr hier unbedingt beachten müsst, lest ihr in einem später erscheinenden Blog-Eintrag.
Fassen wir zuletzt noch einmal mögliche Kriterien für die Einführung von querschnittlichen Bounded Contexts kurz zusammen:
In einem zweiten Teil dieses Blogs diskutieren wir, welche Prinzipien und Werkzeuge hilfreich für die Umsetzung eines solchen querschnittlichen Bounded Contexts sind. Dabei wollen wir uns insbesondere auf den Entwurf der nötigen Schnittstellen fokussieren.