DDDLDie worldiety Domain-Driven Design Language kann zur Unterstützung einer strukturierten Anforderungserhebung dienen. Dazu wird eine Interpretation verschiedener Elemente basierend auf den strategischen Mustern des Domain-Driven Design mit Hilfe einer domänenspezifischen Sprache bereitgestellt. Grundsätzlich tragen die TxT-Dateien die Endung *.ddd und können nach Belieben arrangiert und verschachtelt werden. InstallationDamit die Vorschaugenerierung richtig funktioniert, ist plantuml und der BPMN-Font erforderlich:
Download und Installation der bpmn.ttf aus dem bpmn-font Projekt. KommandosMit shift+cmd+p öffnet sich die Kommandopalette von VSC. Die folgenden Kommandos stehen zur Verfügung
DevOps ToolsDas Projekt enthält neben dem VSC-Plugin und dem LSP-Server auch die folgenden Tools. dddcDer DDD Compiler für das automatisierte Erzeugen von verschiedenen Ausgabeformaten kann wie folgt installiert werden:
Werkzeug für WorkshopsDieses Werkzeug eignet sich als Ergänzung für die Techniken des Event Stormings, des Domain Story Tellings sowie der BMPN. Es erscheint sinnvoll den ersten Workshop mit allen Stakeholdern zu machen und alle Prozesse mit ihren Beteiligten aufzunehmen. In den dann folgenden Workshop sollten in Einzelinterviews mit den jeweiligen Domänenexperten:innen die exakte Daten und Ablaufdokumentation entstehen. Erst hiernach können sinnvolle User Stories mitsamt Akzeptanzkritieren formuliert werden. Neue Erkenntnisse und Fragen aus dem Refinement müssen in dem gemeinsamen ddd-Modell wieder ergänzt werden, um das gemeinsame mentale Modell fortwährend aktuell zu halten. Event StormingDiese Methode definiert ein PostIt-basiertes Workshop-Verfahren, das extra zur Erkundung der Domäne (Fachlichkeit) im Domain-driven Design entworfen wurde. Kernidee ist, dass man alle Stakeholder in einem Workshop zusammenbringt und von den Arbeitsergebnissen her denkt. Dazu wird zuerst gezielt nach Domänen-Ereignissen gefragt (Domain Events). Alle Stakeholder zu beteiligen ist wichtig, da auch diese üblicherweise in ihren eigenen Bounded Contexten professionalisiert sind und daher auch nur jeweils Teile von Geschäftsprozessen wissen. Domänen-Ereignisse sind in der Vergangenheitsform zu formulieren, als etwas, dass eingetreten ist. Nachdem alle wesentlichen Ereignisse identifiziert wurden, wozu auch Fehlerzustände gehören, werden in folgenden Workshop-Iterationen die Akteure (Actors), deren Kommandos (Commands) und Ansichten (Views) bestimmt. Es können auch noch Elemente für Aggregate-Roots und Externe Systeme festgelegt werden. Im letzten Schritt kann eine erste Gruppierung in Subdomänen bzw. Bounded Contexte erfolgen. Das Verfahren ist weder gedacht noch dazu geeignet, um komplexe Prozesse im Detail zu dokumentieren, beispielweise mit Schleifen und Sonderfällen. Schwierig erscheint auch die Bedeutung in der Kommunikation und im späteren Entwicklungsprozess. Mit diesem Verfahren erkundete Domänen, fordern eine ereignisbasierte gemeinsame Sprache (ubiquitous language) und damit auch eine CQRS-Architektur (Command-Query-Responsibility-Segregation) mit Event Sourcing. Ansonsten ist ein kompletter Bruch zu erwarten, der ein Auseinanderdriften der Dokumentation und Implementierung stark begünstigt. Domain Story TellingDiese Methode definiert ebenfalls ein Workshop-Verfahren zur Identifizierung der relevanten Prozesse und beschreibt diese durch das Festlegen von Akteuren, Aktivitäten und Arbeitsobjekten. Ein Akteur nimmt dazu üblicherweise die Rolle des Subjektes ein, die Aktivität die des Verbs und das Arbeitsobjekt stellt das Objekt dar, sodass einfach zu lesende gerichte Flussgraphen entstehen. Damit diese später besser nachvollzogen werden können, muss die Ausführungsreihenfolge durch eine Nummerierung dokumentiert werden. Abschließend werden die Bounded Contexte bestimmt und durch das Zeichnen von entsprechenden Begrenzungsrechtecken dargestellt. Das Verfahren ist weder gedacht noch dazu geeignet, um komplexe Prozesse für Dritte zu dokumentieren. Stattdessen ist es nur als Gedankenstütze für die Workshopteilnehmer gedacht. Insbesondere fehlen Möglichkeiten Sonderfälle und Varianten abzubilden. Dazu wird empfohlen, entweder mit Kommentaren zu arbeiten oder Prozesse zu kopieren und jede Variante in einer eigenen Grafik darzustellen. Hierbei ist kritisch zu bedenken, dass der kombinatorische Aufwand eine nicht mehr beherrschbare Komplexität verursacht, sodass sich das Verfahren wirklich nur für stark vereinfachte Prozessdarstellungen eignet. BPMNDie Business Process Model and Notation ist eine grafische Notation zur Dokumentation von Geschäftsprozessen und wie die UML durch die OMG standardisiert. Diese Notation ist durch ihren Umfang und Komplexität nur bedingt workshoptauglich, dafür aber besonders geeignet um in Einzelinterviews mit Domänenexperten einen Arbeitsablauf mit allen notwendigen Facetten zu dokumentieren. Ziel ist die voll umfassende Prozessdokumentation, sodass auch Dritte in der Lage sind, einen Prozess nachzuvollziehen und ggf. selbst ausführen zu können. FazitDie hier unterstützte Notation für Arbeitsabläufe soll ebenso in Domänen-Erkundungs-Workshops verwendbar sein, wie das Event Storming und Domain Story Telling aber auch ausdrucksstark genug sein, um im Nachgang alle fachlichen Sonderfälle für die eigentliche Entwicklung ähnlich der BPMN zu dokumentieren. Dabei beträgt der Umfang allerdings im Vergleich zur BPMN nur einen Bruchteil, ist aber sprachlich wie technisch ausreichend vollständig. Durch die Verwandtschaft mit dem Event Storming dominiert auch hier eine ereignisorientierte Modellierung, die zu einem Bruch im gemeinsamen mentalen Modell führen kann. Allerdings kann hier explizit in der fachlichen Modellierung zwischen der Referenzierung mittels Bezeichnern und beschreibenden Fließtexten unterschieden werden. Wenn also wirklich ein CQRS-System fachlich modelliert werden soll, muss ein Ereignis als Daten Deklaration ausgeführt werden und eben genau dieser Bezeichner auch im Arbeitsablauf verwendet werden. Hierbei ist ganz genau zu klären, welche nicht-technischen aber fachlichen Anforderungen an die Daten- und Prozesskonsistenz zu garantieren sind. Es gibt viele weitere Techniken zur Abbildung von Anforderungen. Dazu zählen die nutzerzentrierten Szenarien sowie User Stories. Diese beschreiben einen Prozess vollständig aus der Sicht des Nutzers. Der hier definierte Arbeitsablauf hat diese Beschränkung nicht, hinterfragt aber auch nicht die Notwendigkeit eines Geschäftsprozesses, der als gegeben hingenommen wird. Daher ist es im Workshop sehr sinnvoll, den Sinn und die Bedeutung eines Prozesses zu prüfen und statt des Ist-Zustands besser den zukünftig erwünschten und ggf. neu digitalisierten Ablauf zu dokumentieren. Beispiel
Strukturierung nach DDDIm Domain-Driven Design werden strategische und taktische Mustern unterschieden. Kontext (engl. context)Die Zerlegung in verschiedene Bounded Contexte gehört zu den strategischen, also organisatorischen Mustern. Dabei steht häufig im Vordergrund, dass die gesamte Domäne (z.B. ein Unternehmen) in verschiedene und möglichst entkoppelte Kontexte aufgeteilt werden kann. Um diese Zerteilung vorzunehmen, gibt es kein Regelwerk, da jede Domäne individuell sein kann. Heuristiken zur Erkennung von verschiedenen Bounded Contexts sind:
Der Projektmanager bestimmt danach die Priorität innerhalb der Projektumsetzung. D.h. es wird mit dem Kunden zusammen bestimmt, welches der wichtigste Bounded Context ist. Von der erfolgreichen Umsetzung dieses Kontextes hängen alle anderen Kontexte ab, was als Upstream-Kontext bezeichnet wird. Upstream und Downstream bezeichnen in dieser Mustersprache nicht den Datenfluss, sondern die Team- bzw. Umsetzungsprioritäten. Beispiel
Aggregat (engl. aggregate)Ein Aggregat bezeichnet ein Muster, das im Wesentlichen die Grenze einer fachlichen Transaktion beschreibt. Hierbei muss mit den fachlichen Experten geklärt werden, welche Konsistenzgarantien wirklich erforderlich sind. Als Daumenregel gilt, dass je größer und umfassender die Anforderungen an die atomare und unmittelbar sichtbare Konsistenz sind, desto unflexibler wird die technische Architektur und desto schlechter lässt sich die Entwicklung mit mehreren unabhängigen Teams organisieren. Kennzeichnend eines Aggregats ist das aggregate root. Hierbei handelt es sich um eine Entität, die über eine eindeutige ID referenziert werden kann. Diese Entität stellt ihre eigene Konsistenz (d.h. die Einhaltung der fachlichen Invarianten) per Information Hiding sicher. Normalerweise ist der Zustand einer Entität auf Seiteneffekte ausgelegt, d.h. mind. das persistente Lesen, Speichern, Bearbeiten und Löschen, was in der Persistenz bzw. Infrastrukturschicht implementiert wird. Aggregate dürfen untereinander nur über ihre ID aufeinander Bezug nehmen. Durch die Schichtentrennung und dem Information Hiding ist ein perfektes Design zudem eigentlich unmöglich technisch auszudrücken: entweder muss das Domänen-Objekt für die Persistenzschicht das information hiding Prinzip verletzten (Deserialisierung und Konstruktion) oder die Domänenschicht erhält Persistenzanteile. Zudem sind Aggregate die Quelle und die Empfänger von Domänenereignissen.
Entwickler:innen können ein Aggregat auf verschiedene Weisen technisch ausdrücken. Dies kann im objektorientierten Stil (Klasse) erfolgen oder auch über eine Reihe von freien (pure) Funktionen möglich sein. Je nach Umfang erscheint häufig ein eigenes Package praktisch, da hierbei das Prinzip des Information Hiding mittels package private Sichtbarkeiten klarer durchgesetzt werden kann. Ein Aggregat ist die kleinstmögliche Einheit für einen Microservice. TypenEin Typ definiert eine Menge von Werten und damit verbundene Eigenschaften und erlaubte Operationen. Boolesche TypenEs ist Absicht, dass es keinen booleschen Typ gibt, da diese immer als Auswahltyp domänenspezifisch definiert werden sollen. Entwickler:innen haben später die Möglichkeit diese je nach Sprache als Konstanten oder eigenständige Typen abzubilden. Schlechtes Beispiel:
Besseres Beispiel:
Zahl (engl. Number)Zahl definiert einen unspezifischen Zahlentyp, der sowohl eine Ganzzahl oder eine Fließkommazahl sein kann. Entwickler:innen müssen daraus einen Auswahltyp machen, um möglichst exakt zu sein.
Ganzzahl (engl. Integer)Eine Ganzzahl definiert eine positive oder negative natürliche Zahl. Sofern der Wertebereich nicht eingeschränkt ist, sollten Entwickler:innen einen int64 bzw long als Basisdatentyp wählen. Gleitkommazahl (engl. Float)Eine Gleitkommazahl definiert einen IEEE Float. Grundsätzlich sollten Entwickler:innen einen float64 bzw. double als Basisdatentyp wählen. Text (engl. String)Ein Text sollte immer eine Byte-Sequenz im UTF-8 Format sein. Andere Formate kommen heute in der Verarbeitung intern eigentlich nicht mehr vor. Die Anbindung von Legacy-Systemen sollte entsprechend klar modelliert werden, sodass offensichtlich ist, wenn es sich nicht um UTF-8 handelt. Je nach Programmiersprache bzw. Plattform müssen Entwickler:innen dann auch abweichende Basistypen bzw. Decoder wählen. Liste (engl. List)Eine Liste ist eine generische Datenstruktur zum Halten einer geordnete Menge von gleichartigen Werten. Beispiel
Zuordnung (engl. Map)Eine Zuordnung ist eine generische Datenstruktur zum Halten von eindeutigen Schlüssel-Wert-Beziehungen. Entwickler:innen sollten die Gleichheit von Schlüsseln anhand der Werte prüfen und nicht an der technischen Eindeutigkeit von Zeigern oder Referenzen. Beispiel
Menge (engl. Set)Eine Menge ist eine generische Datenstruktur zum Halten von eindeutigen Werten. Die Reihenfolge ist undefiniert. Entwickler:innen sollten die Gleichheit von Werten prüfen und nicht an der technischen Eindeutigkeit von Zeigern oder Referenzen. Beispiel
Auswahl (engl. choice)Ein Auswahltyp ist ein algebraischer Koprodukt-Typ, bei dem nur jeweils genau einer der definierten Typen angenommen werden kann (disjunkt). Entwickler:innen kennen dies aus anderen Sprachen z.B. als Sealed Type (Java), enum (Rust) oder per |-Notation (Typescript). Im Grunde lässt sich dies in jeder Sprache mit Polymorphismus abbilden, entweder über klassische Vererbung oder mittels Interfaces. Beispiel
Daten (engl. data)Ein Datentyp ist ein algebraischer Produkttyp, bei dem alle Elemente gleichzeitig vorhanden sind. Siehe auch Auswahltyp. Entwickler:innen kennen dies als technisches Struct, Record oder Class. Beispiel
Häufig kommt es in Geschäftsprozessen zu einem mehrstufigen Arbeitsablauf, der Daten in mehreren Schritten transformiert. Diese sollten wann immer möglich so exakt wie möglich notiert werden. Optionale Felder sind dann meist unnötig und ein Zeichen schwacher oder gar falscher Domänenmodellierung. Schlechtes Beispiel:
Besseres Beispiel:
AnnotationenAnnotationen verwenden die Bezeichnergrammatik und erlauben eine flexible Anzahl an Schlüssel-Wert Paaren zu definieren. Dadurch sind keine weiteren belegten Schlüsselwörter in der Sprache erforderlich und das System lässt sich einfacher erweitern. Derzeit sind die folgenden Annotationen spezifiziert. @Ereignis (engl. @event)Domänenereignisse dienen insbesondere auch der Kommunikationen zwischen Bounded Contexten. Innerhalb eines Bounded Context ist das Erzeugen und Konsumieren von eigenen Domänenereignissen je nach Architektur mitunter nur unnötiger Aufwand und die derzeitige Empfehlung ist es, diese eher zu vermeiden. Für eine starke Entkopplung von Aggregaten oder ganzen Bounded Contexten eignet sich auf Kosten von zusätzlicher Duplikation und mit der Einführung von eventueller Konsistenz allerdings sehr gut. Um noch spezifischer zu formulieren, sollte daher jedes Ereignis noch um Eigenschaften ergänzt werden, die anzeigen, ob es sich um ein- oder ausgehende Ereignisse handeln. Beispiel
Entwickler:innen wissen dann, dass für die zu implementierenden Datenstrukturen ein technischer Adapter an eine Event Sourcing Architektur erstellt werden muss. Typischerweise sollte ein Bounded Context oder mind. ein Microservice mit einem einzelnen Aggregat, seinen eigenen Event Store als Audit-Log halten sowie Event-unabhängige Persistenz, das die Single Source of Truth enthält. Hintergrund sind die folgenden Argumente:
@Fehler (engl. @error)Fehlerzustände bzw. fehlerartige Randfälle sollten explizit als Fehler gekennzeichnet werden. Entwickler:innen modellieren dies in der Domäne später ggf. auf sprachspezifische Weise beispielsweise mit errors oder Exceptions. Beispiel
@Fremdsystem (engl. @error)Ein Bounded Context interagiert zwecks Seiteneffekte eigentlich immer mit externen Fremdsystemen. Dazu können neben klassischen Systemen wie E-Mails oder Notifications auch Infrastruktur-Themen gezählt werden, wie das Persistieren in einer Datenbank oder in S3. Wird also ein Fremdsystem aus der Sicht der Domänenmodellierung identifiziert, so sollte dafür eine Aufgabe ohne Block definiert werden. Entwickler:innen wissen dann, dass innerhalb der Domänenimplementierung nur ein Funktionstyp bzw. ein Interface erstellt werden muss und die eigentliche Implementierung später per Injection reingereicht wird. Beispiel
GrammatikRoadmap
|