Strategisches DDD
Die Architektur von Schleupen.CS 3.0 setzt auf den Mustern und Prinzipien des Domain-Driven Designs (DDD) auf. DDD unterschiedet zwischen strategischem und taktischen Design. Für die Makroarchitektur der Plattform ist das strategische DDD relevant. Es beschreibt, die Beziehung der Teams und deren Bausteine miteinander. Ziel ist es, eine parallele, möglichst eigenständige und an relevanten Stellen auch koordinierte Arbeit verschiedener Teams an unterschiedlichen Teilen einer Domäne zu ermöglichen. Dies ist für uns ein entscheidendes Kriterium, da aktuell ca. 180 Entwickler in ca. 20 Teams an Schleupen.CS arbeiten.
Wir gehen davon aus, dass eine sehr große Anwendungslandschaft nicht von einem einzigen einheitlichen Modell so vollständig beschrieben werden kann, dass dieses als hinreichende Grundlage für eine Implementierung dienen kann. Dieser Grundgedanke spielt auch bei DDD eine wichtige Rolle. Gemäß DDD gliedert sich die Anwendungslandschaft in fachliche Domänen und Subdomänen. Deren Repräsentation in Schleupen.CS 3.0 haben wir in Form von Kontinenten und Ländern bereits kennengelernt. Werfen wir nun einen Blick auf eines der zentralen Muster des strategischen DDD, den Bounded Context.
Bounded Context
Ziel der Bemühungen ist es,
- den Fokus bei der Umsetzung auf das fachliche Domänenwissen zu setzen und dies möglichst gut in Code zu transferieren -
die Datenbank steht nicht im Zentrum der Implementierung - Missverständnisse und Begriffsverwirrungen zwischen den an der Entwicklung beteiligten Personen zu vermeiden und strenge sprachliche Modelle zu entwerfen, die dem Sprachgebrauch der Nutzer entsprechen
- klare Zuständigkeiten bezüglich der Entwicklungsaufgaben zu definieren, Team-Abhängigkeiten zu beschreiben und Team-Kopplungen schon auf Modell-Ebene zu minimieren
Um diese Ziele zu erreichen, nutzt DDD u.a. das Konzept des Bounded Context. Ein Bounded Context ist ein abgegrenztes fachliches sprachliches Modell, welches einen zusammengehörenden Teil der Domäne, einen fachlichen Kontext, definiert, welches von (genau) einem Entwicklungsteam verantwortet wird. Im Umfeld von Schleupen.CS 3.0 sind Länder und Geschäftsprozess-Komponenten jeweils Bounded Contexts.
Bounded Contexts werden designt, definieren also den Lösungsraum, während Domänen / Subdomänen den Problemraum beschreiben und eher gegeben sind!
Ein Bounded Context wird durch ein Modell beschrieben. Die in diesem Modell verwendeten Begriffe entstammen einer für diesen Bounded Context gültigen "allgegenwärtigen Sprache", einer Ubiquitous Language. Diese Sprache (bestehend aus den fachlichen Begriffen der Domäne) muss für das umsetzende Team und die Domänenexperten eindeutig sein. Sie ist "allgegenwärtig", da sie nicht nur für verbale Kommunikation, sondern auch im Code, in Dokumentation usw. verwendet werden soll, damit Transferleistungen und daraus folgende Uneindeutigkeiten vermieden werden.
Da auch die den Bounded Context beschreibenden Modelle Teile der Domäne beschreiben und methodisch identisch erstellt werden können wie Modelle die eine gesamte Domäne beschreiben, verwenden wir den Begriff Domänenmodell im weiteren Verlauf insbesondere für Teilmodelle, die "nur" einen Bounded Context beschreiben. Im DDD versucht man im allgemeinen kein allumfassendes Modell zu beschreiben, sondern dieses zu unterteilen. Typischerweise sprechen wir daher bei einem Domänenmodell als das mit objektorientierter Programmierung umgesetzte Modell der Domäne innerhalb eines Bounded Contexts. Weichen wir hiervon ab, wird hierauf hingewiesen.
Die Fachdomänen von Schleupen.CS entstammen in der Regel dem deutschen Energiemarkt. Dieser Markt ist stark reguliert. Da die Regulierung nur für den deutschen Markt gilt, sind auch die verwendeten Begriffe deutsch. Aus diesem Grund verwendet Schleupen.CS deutsche Begriffe in den Domänenmodellen. Eine Übersetzung, z.B. ins Englische, würde von den Beteiligten eine Transferleistung verlangen, die ggf. zu nicht eindeutigen Ergebnissen führen würde. In keinem Fall wäre die Sprache allgegenwärtig, da die Begriffe in der Dokumentation der Regulatorik nicht identisch zu denen im Code wären. Die Infrastrukturteile der Plattform, entstammen einer eher technisch geprägten Domäne, in der die englische Sprache vorherrschend ist. Die Begriffe sind aus diesem Grund hier englisch.
Interessierte finden eine Einführung in das Thema beispielsweise in https://www.domainlanguage.com/ddd/.
Kapselung der Domänenmodelle
Um die in Schleupen.CS geforderte Flexibilität zu erreichen und eine Autonomie der entwickelnden Teams zu erreichen, streben wir eine lose Kopplung der Schleupen.CS Bausteine an. Es ist zu erwarten, dass allgemeine Begriffe der Fachsprache (z.B. Vertragspartner) in verschiedenen Bounded Contexts verwendet werden. Es ist wichtig zu verstehen, dass es dennoch nicht die eine übergreifende Implementierung der Klasse Vertragspartner für alle Anwendungsszenarien und Bausteine gibt. Jeder Bounded Context hat sein eigenes sprachliches und somit implementiertes Modell, das auch zur Dopplung einer Klasse Vertragspartner mit unterschiedlicher Logik führt. Demzufolge gilt eine Ubiquitous Language nur für seinen Bounded Context. Ziel ist es, das Domänenmmodell des Bounded Contexts zu kapseln.
Die innere Implementierung der Bounded Contexts ist Teil des taktischen DDD und wird hier nicht behandelt.
Da die Bounded Contexts aber nur Teile einer Domäne sind, stellt sich die Frage, in welcher Beziehung diese Kontexte stehen und wie diese untereinander kommunizieren. Kontakt nach außen haben die Domänenmodelle in Schleupen.CS über den Service Layer. Auf Kommunikationsebene zwischen den Kontexten werden sogenannte Business-Entitäten ausgetauscht. Diese sind also nicht Teil des Domänenmodells des Bounded Contexts. Dabei wird bei jeder Kommunikation nur der jeweils relevante Teil einer Business Entität (z.B. die für ihn relevanten Properties eines Vertragspartners) für die jeweilige Operation verwendet. Die Implementierung des Domänenmodells, der nach außen propagierten Business Entitäten und der für diesen Bounded Context benötigten Funktionalität ist von außen betrachtet, hinter dem Service-Layer verborgen, er wirkt wie eine Service-Fassade.
Harmonisierte Published Language
Die Service-Fassade nutzt nach außen nicht die Ubiquitous Language, sondern eine öffentliche Published Language des Bounded Contexts. Diese erlaubt Dritten die Nutzung des Bounded Contexts. Das "Vokabular" der Published Language wird in der Service-Schnittstelle verwendet und als Business Entitäten nach außen gelegt. Es liegt nahe, das "Vokabular" der Published Languages der Bounded Contexte einer Domäne für die Nutzung durch Dritte zu harmonisieren, um deren Nutzung im Rahmen der Kommunikation zu vereinfachen und unnötige Inkonsistenzen zu vermeiden.
Eine Besonderheit des deutschen Energiemarktes ist der starke regulatorische Einfluss durch die Bundesnetzagentur. Dieser wirkt über die Marktkommunikation für Strom und Gas, der alle Marktpartner verpflichtet sind, bis auf die Implementierungsebene. Neben den Beschreibungen der Prozesse, enthalten die Vorgaben viele Festlegungen zu Begriffen, Datentypen und -größen. Es ist sinnvoll, diese Festlegungen auch bei der Kommunikation zwischen Bounded Contexts (Ländern und Geschäftsprozesskomponenten) zu berücksichtigen.
Schleupen.CS definiert zu diesem Zweck ein Kanonisches Datenmodell (KDM) als reines Analysemodell. Dieses Modell sammelt die in den Public Languages genutzen Properties von Business-Entitäten an zentraler Stelle und fasst diese zu einem Modell zusammen. Die "Vokabeln" in diesem Modell, werden für die Definition der Published Languages neuer Service-Fassaden genutzt.
Es ist wichtig zu realisieren, dass eine Published Language zu einer Service-Fassade und damit zu einem Bounded Context gehört. Sie ist somit keine allgemeingültige Sprache für die gesamte Domäne. Nutzt eine Methode in einer Service-Fassade eine Business-Entität, werden nur die für diese Fassade relevanten Properties ausgewählt und damit Teil der Published Language - alle anderen werden weggelassen.
Die Harmonisierung besteht darin, dass das Vokabular aller Published Languages die zu einer Schleupen.CS Domäne gehören, auf das eine Kanonische Datenmodell zurückzuführen ist. Dieses Vorgehen vereinfacht die Definition von Schnittstellen und verhindert, dass inkompatible Datentypen oder -längen innerhalb der Plattform verwendet werden. Doppeldeutigkeiten werden vermieden.
Ein Modell einer Service-Fassade, welches auf dem Kanonischen Datenmodell von Schleupen.CS basiert, könnte man als kanonische Service-Fassade bezeichnen, deren Gesamtheit nennen wir das Kanonisches Servicemodell (KSM). In den Methoden der Schnittstellen sind nur die für den Aufruf relevanten Properties der zugehörigen Business-Entität enthalten. So kann ein Vertragspartner ggf. nur durch eine Vertragspartner-ID repräsentiert werden. Das Kanonische Servicemodell definiert die Published Language des Bounded Context.
Es ist wichtig zu verstehen, dass das Kanonische Datenmodell von Schleupen.CS 3.0 nur der Harmonisierung der Published Languages dient und nicht in einem Xml-Schema definiert wird, in einer DB instantiiert wird oder als großes Domänenmodell genutzt wird. Es wird nur für die Modellierung genutzt. Es kann verstanden werden als das "Wörterbuch" für die Erstellung der Kanonischen Servicemodelle, auf deren Grundlage Services implementiert werden.
Einige Veröffentlichungen betrachten die Published Language als eigenständiges Kommunikationspattern (vgl. https://vaadin.com/blog/ddd-part-1-strategic-domain-driven-design). Wir verstehen sie allgemeiner als die Sprache der veröffentlichten Schnittstelle und somit als Mittel zur Implementierung eines Kommunikationsmusters.
Ggf. interessant: 'Was ist eigentlich ein kanonisches Datenmodell?'. Wichtig: die "klassische" Sichtweise und Nutzung von kanonischen Datenmodellen unterscheidet sich von unserer Sichtweise. Unser Modell ist lediglich auf Analyseebene kanonisch.
An Nahtstellen der Plattform zu anderen Systemen können Inkompatibilitäten weiterhin vorkommen. In diesen Fällen ist ein Mapping der Daten erforderlich.
Beziehungen zwischen Bounded Contexts
Beziehungen zwischen verschiedenen Bounded Contexts können verschiedene Kommunikationsmuster (Patterns) verwenden. Für Schleupen.CS sind folgende Muster von besonderer Bedeutung:
- Open Host Service (OHS)
Ein Bounded Context stellt Funktionalität einem oder mehreren anderen Bounded Contexts über eine explizite Schnittstelle zur Verfügung, die jeder nutzen kann. Sowohl die Schnittstelle, als auch die verwendeten Protokolle sind klar definiert. Typische Vertreter dieses Musters sind WebServices und Business Events. Die verwendete Published Language wird durch das zugehörige Kanonische Servicemodell beschrieben. - Anti Corruption Layer (ACL)
Dieses Muster wird im Kontext von Schleupen.CS insbesondere von Nutzern einer Schnittstelle eingesetzt, um den eigenen Bounded Context gegen Änderungen der genutzten Schnittstelle zu schützen. Verhindert werden sollen solche Änderungen, die auf das Domänenmodell und die Ubiquitous Language "durchschlagen" würden. Die Hoffnung ist, dass der Nutzer alleine durch Änderungen an der ACL angepasst werden kann. In jedem Fall soll eine Korruption durch die Änderung in diesem Layer auffallen, um z.B. Folgefehler (z.B. falsche Daten) durch eine nicht bemerkte Korruption zu vermeiden.
Wie wir schon im Falle der Sprachen (Ubiquitous und Published Language) gesehen haben, beschäftigt sich DDD nicht "nur" mit Software-Architektur, sondern explizit auch mit den Teams die die Software entwickeln. Demzufolge werden Beziehungen zwischen Bounded Contexts auch als Beziehung zwischen den für sie zuständigen Teams verstanden (vgl. Domain-Driven Design und Bounded Context — Eigentlich ganz einfach, oder? – INNOQ).
Die Länder von Schleupen.CS stellen Bounded Contexts dar, die das OHS Muster nutzen, um Ihre Funktionalität über WebServices und Business Events zur Verfügung stellen.
Würde man die Beziehungen zwischen den Bounded Contexts von Schleupen.CS in einer Context Map - einer graphischen Darstellung der Beziehungen - darstellen, würde man feststellten, dass diese durch die einheitliche Nutzung der Muster OHS und ACL sehr homogen ist.
Da auch die Published Language der Service-Fassade des Bereitstellers möglichst stabil sein soll, ist diese ebenfalls durch einen Anticorruption Layer von der Ubiquitous Languge entkoppelt. Dieser ist in den Diagrammen nicht eingezeichnet, da er für das Verständnis der Kommunikation zwischen den Kontexten nicht relevant ist.
Synchrone Kommunikation - Service-Aufrufe
Anbieter und Nutzer der Schnittstelle können Länder oder GP-Komponenten sein.
Das Context-Mapping für die synchrone Kommunikation nutzt im Standard folgende Muster:
- Ein Land / eine GP-Komponente (in der Regel der Anbieter) dokumentiert die Serviceschnittstellen als Published Language (PL) in Form einer WSDL die aus dem Kanonischen Servicemodell (UML) entstanden ist, technisch aber mit Details zum Protokoll etc. angereichert wurde.
- Der Anbieter ist Bereitsteller eines Open Host Service (OHS) und registriert diesen im Service Bus.
- Der Nutzer hat einen Anticorruption Layer (ACL).
Asynchrone Kommunikation - Business Events
Die Context-Map für asynchrone Kommunikation hat also folgende Gestalt:
- Ein Land / eine GP-Komponente (i.d.R. der Event-Anbieter / -Auslöser) dokumentiert die Event Service-Schnittstelle als Published Language (PL) in Form einer WSDL und veröffentlicht diese im Service Bus.
- Der empfangende Bounded Context implementiert einen Service als Open Host Service (OHS) und registriert sich damit im Service Bus als Event-Empfänger (Subscription).
- Der Event-Service, der als Subscriber fungiert, hat einen Anticorruption Layer. Dieser verhindert, dass Anpassungen an der Published Language nicht unmittelbar in den Bounded Context durchschlagen.
- Löst der Anbieter das Business Event aus, ruft dieser den Service Bus auf, welcher dann alle empfangenden Services (im Bild nur einer) aufruft.
Der Event-Auslöser hat ebenfalls einen Anitcorruption Layer. Da dieser für die Team-Kommunikation nicht so relevant ist, wurde dieser nicht eingezeichnet.
An einigen wenigen Stellen verwendet Schleupen.CS auch andere Kommunikationsmuster wie beispielsweise Shared Kernel, Customer Supplier etc. Insofern ist obige Darstellung der Standard.