Inside DDD – Domain Driven Design erklärt anhand von Schachmodellen

· Philipp
Schach

Dieser Artikel ist der zweite Teil meines Berichts vom Software Architecture Summit Munich 2020. Den ersten Teil findest du hier.

Ob Test Driven Development (TDD), Behaviour Driven Development (BDD) oder Beer Driven Development – zwei “D” sind schon länger in Mode. Domain Driven Design macht die Alliteration perfekt und bringt mit dem dritten „D“ ein wenig mehr Tiefe ins Spiel. Domain bedeutet übersetzt Fachbereich, Fachgebiet oder Fachlichkeit. Das klingt leicht angestaubt und schreit auch im Deutschen nach einer modernen Vokabel – bei uns im Team sagen wir einfach Domäne. Trotzdem gibt uns die altehrwürdige Translation einen überaus wichtigen Hinweis: Hier geht es in erster Linie nicht um Technik. Hier geht es um Expertise, um Sorgfalt und Korrektheit, aber nicht um Technik. Und genau das ermöglicht uns, die Domäne, zusammen mit den Nicht-Technikern, mit den Fachleuten und Experten, zu erforschen und zu verstehen, bevor wir uns um die Implementierung kümmern. DDD gibt uns ein Modell an die Hand, um gemeinsam mit den Experten Probleme, Wertströme und Geschäftsprozesse zu durchleuchten. Alberto Brandolinis Event Storming ist sicher die einfachste und bekannteste Methode für den Anfang, später kann man ohne Probleme tiefer ins Detail gehen. Bei jobvalley verwenden wir derzeit eine Eigenkreation, inspiriert von Adam Dymitruks Event Modeling. Doch auch User Story Maps, Customer Journeys oder sogar Business Process-Diagramme lassen sich mit ein bisschen Übung schnell in ein Domain Model überführen. Erstaunliche Parallelen findet man z.B. in der BPMN Specification.

Commands und Events

Im Herzen eines jeden Modells finden wir Ketten aus blauen und orangen Zetteln. So prägend ist diese visuelle Unterscheidung, die Alberto Brandolini mit seinem Event Storming zur Konvention gemacht hat, dass die Farben sich in meinem Kopf schon wie selbstverständlich mit Aktionen und Ereignissen verbinden, wann immer es darum geht, einen Prozess zu skizzieren. Blau und Orange. Command und Event. Commands und Events sind die Kernkonzepte beim erschließen der Fachlichkeit eines Geschäftsbereiches. Sie sind das Ergebnis der Diskussion mit den Experten und diese Diskussion sollte bis aufs Blut geführt werden, denn nur so erlangt man ein gemeinsames Verständnis, nur so deckt man Wahrheiten und Überraschungen auf in einem System, was seit 30 Jahren „schon immer so“ funktioniert hat, dafür aber im Arbeitsalltag bemerkenswert kompliziert und frustrierend daherkommt. In der Praxis hat sich gezeigt, dass es sich lohnt, ein besonderes Augenmerk auf die verwendeten Begriffe und Bezeichnungen zu legen und Ungereimtheiten als Anlass genauerer Betrachtung zu nehmen.

Anhand eines Schachspiels (Dank an Golo Roden für dieses eingängige Beispiel) möchte ich das Zusammenspiel von Commands und Events innerhalb der Grenzen eines Aggregates erklären und veranschaulichen, wie sich Begrifflichkeiten im Zuge dessen schärfen. Ein Aggregat stellt im DDD einen Rahmen dar. Innerhalb herrscht Konsistenz und es gelten feste Regeln, außerhalb tobt das Chaos. Um Konsistenz zu gewährleisten, verfügt das Aggregat über einen Zustand. Dieser Zustand ermöglicht es, Aktionen (Commands) auf dem Aggregat zu verarbeiten und Ereignisse (Events) zu projizieren.

Ein Schachspiel als Aggregat betrachtet, kommt mit einem Regelwerk daher, das den Spielern erlaubt, ihre Figuren auf bestimmte Art und Weise zu bewegen. Das Bewegen einer Figur ist eine Aktion des Spielers. Das zugehörige Command in unserem Modell lautet Bewege Figur. Um das Command ausführen zu können, müssen wir noch wissen, welche Figur von wo nach wo bewegt werden soll. Jetzt kann das Aggregat anhand seines Regelwerks überprüfen, ob das Command zulässig ist. Wenn der Spieler zur Eröffnung einen Bauern um ein Feld vorzieht, ist das erlaubt. Selbst das Vorziehen um zwei Felder ist zu diesem Zeitpunkt – oder besser: in diesem Zustand – des Spiels gestattet. Somit hätte die Verarbeitung des Commands Bewege Figur ein Event zur Folge: Figur bewegt.

Commands werden immer als Befehle formuliert und Events immer in der Vergangenheit. Das Event Figur bewegt ist die unumkehrbare Reaktion des Schachspiels auf die Aktion des Spielers, denn aufgrund des Ereignisses wird auch der Zustand des Spiels angepasst. In unserem Fall dient das Schachbrett mit seinen Figuren als plastische Repräsentation des Spielzustands, in einem Schachcomputer würde die neue Position entsprechend in einer Liste vermerkt. Wenn der Spieler allerdings beim nächsten Mal versucht, seinen Bauern nicht um ein oder zwei, sondern um drei Felder vorzuziehen, dann wird unser Schachspiel-Aggregat (vermutlich in Person eines ungehaltenen Gegenspielers) diese Aktion als ungültiges Command zurückweisen und seinen Zustand nicht verändern.

Mit seinem Set von Regeln, das die Aktionen der Spieler validiert und seinem Zustand, der die Auswirkungen der Ereignisse (der Bewegungen der Figuren) repräsentiert, hat unser Schachspiel-Aggregat also das nötige Rüstzeug, um eine Partie durchzuführen. Im Verlauf der Partie werden als Folge von Aktionen noch weitere Ereignisse eintreten, wie Figur geschlagen oder Gegner ins Schach gesetzt, welche ebenso wie Figur bewegt Auswirkungen auf den Zustand des Spiels haben und gegebenenfalls weitere Regeln erfordern, doch sie alle können nach dem gleichen Schema ins Aggregat aufgenommen werden.

Wenden wir uns an dieser Stelle noch einmal unserem ersten Command zu und fühlen ihm ein bisschen auf den Zahn, um seine fachliche Korrektheit zu prüfen. Nicht nur Magnus Carlsen hätte wahrscheinlich schon längst eingeworfen, „beim Schach macht man einen Zug, da bewegt man keine Figuren!“. Natürlich bewegt man in letzter Konsequenz eines Zuges immer noch die Figur, aber genau auf diese Details der Fachsprache kommt es an. Und für eine Rochade (ein besonderer Zug, bei dem König und Turm eines Spielers gleichzeitig bewegt werden) reicht es tatsächlich auch nicht aus, nur eine Figur zu bewegen. Unser Command müsste also eher Führe Zug aus heißen und das daraus resultierende Event Zug ausgeführt. Erforscht man die Domäne noch weiter, dann wird man zum Beispiel auf Wikipedia fündig und findet folgenden Auszug:

„In der Schachnotation werden immer eine weiße und eine anschließende schwarze Figurenbewegung zusammen nummeriert und als Zug gezählt. Aus dem Zusammenhang ist üblicherweise ersichtlich, welche Bedeutung des Wortes Zug gemeint ist. Sollte aber eine genaue Begriffsunterscheidung nötig sein, nennt man die Aktion des einzelnen Spielers Halbzug.“ (Quelle: Wikipedia)

Es gibt also für die Notation eines Schachspiels eine konkurrierende Definition für einen Zug. Wenn wir unser Modell erweitern möchten, um Schachspiele nicht nur durchführen, sondern auch dokumentieren zu können, müssten wir Command und Event ein weiteres Mal anpassen in Führe Halbzug aus und Halbzug ausgeführt. Eine zweite, von mir präferierte Möglichkeit bestünde darin, die Schachnotation und das Schachspiel selbst als getrennte Kontexte zu betrachten. Diese Bounded Contexts, von denen es oft mehrere innerhalb einer Domäne gibt, lassen sich gut über solche voneinander abweichenden Begriffsdefinitionen abgrenzen. Die Experten für einen einzelnen Kontext verwenden eine ihnen eigene Sprache, die sogenannte Ubiquitous Language. Hier befinden wir uns am Übergang von der Mikro- zur Makroperspektive des Domain Driven Design.

Im dritten und letzten Teil meines Berichts aus München werde ich mich mit Event Sourcing und CQRS auseinandersetzen. Wenn ihr Feedback oder Fragen habt, kontaktiert mich gerne auf Twitter.