Auf einen Blick
- Ausgangspunkt ist der wichtigste Befund aus dem Code Review des NGHR-Durchstichs: das Antipattern Blutleere Domaene (anemic domain layer) – eine Domaenenschicht, die zwar Daten haelt, aber kein fachliches Verhalten hat.
- Domain Driven Design (DDD, Eric Evans 2004) ist die Antwort auf dieses Antipattern und gibt der fachlichen Logik wieder eine Heimat im Code.
- DDD hat zwei Ebenen: die Strategische Ebene (Kontext klaeren, Wissen sammeln, Allgegenwaertige Sprache, Domaene in Subdomaenen mit Kontextgrenzen aufteilen) und die Taktische Ebene (das reichhaltige Modell im Code ausdruecken).
- Der Anwendungskern wird in drei Schichten/Hexagons geteilt: Domaene, Applikation, Infrastruktur (aus der logischen Sicht).
- Der Baukasten der Domaene: Entitaeten, Wertobjekte, Domain Service, Domain Event, Aggregate. DDD liefert fuer Applikation und Infrastruktur keine Konzepte – diese Luecke schliesst das Hexagonale Modell mit Ports und Adaptern.
- Leitprinzip: Domaene zuerst! – Steuerung, Benutzerschnittstelle und Datenbank sind Details, um die man sich spaeter kuemmert.
Kernkonzepte
1. Ausgangslage: Code Review des NGHR-Durchstichs
Das durchgaengige Beispiel ist NGHR (Next Generation HR), ein HR-System der fiktiven Firma Sonnenschein. Ein Durchstich (Durchstich = duenner Schnitt durch alle Schichten) wurde implementiert und im Code Review geprueft, ob er eine tragfaehige Ausgangslage ist. Anwendungsfaelle: HR-Mitarbeiter legt neuen Mitarbeiter an; Mitarbeiter aendert seine Adresse im Self-Service. Die HTTP-Schnittstelle existiert nur als Klasse EmployeeHttpInboundAdapter (noch nicht an HTTP gebunden – das ist kein Befund). Persistenz ist durch ein In-Memory-Repository simuliert.
2. Befunde des Code Reviews (technische Maengel)
Gefundene Maengel: Felder von Employee sind Java-Standardfelder – Konzepte sind nicht als Klassen modelliert. DIP-Verletzung: Abhaengigkeit von Implementierungen, weil Implementierungen per new in den Clients erzeugt werden (EmployeeInboundPort, EmployeeServiceImpl). Kein ISP fuer HR-Client und Self-Service-Client im EmployeeHttpInboundAdapter. Die Domaenenklasse EmployeeServiceImpl haengt von Infrastruktur (OutboundAdapter) ab. EmployeeRepositoryOutboundPort ist in der Domaene deklariert. EmployeeServiceImpl kuemmert sich um die Ablaufsteuerung (Lesen und Speichern von Employee). Das Geschaeftsobjekt Employee wird durch den EmployeeHttpInboundAdapter exponiert.
3. Der wichtigste Befund: Blutleere Domaene (Anemic Domain)
Antipattern anemic domain layer. Employee ist blutleer: unvollstaendige Erzeugung, Invarianten sind nicht eingehalten, kein fachliches Wissen, keine fachlichen Regeln. Auch EmployeeService ist blutleer: die universelle save-Methode enthaelt keine fachlichen Regeln, sie weiss nicht, in welchem Kontext sie aufgerufen wurde (Mitarbeiter anlegen, Adresse aendern, ...) und kann daher keinen fachlichen Anwendungsfall sinnvoll aufnehmen. Folge: Die fachliche Logik hat keine Heimat.
4. Warum die Blutleere Domaene gravierend ist
Nichts garantiert die Invarianten des Geschaeftsobjekts Employee – es ist kein Objekt mit Verhalten, sondern nur eine statische Datenstruktur, auf deren Daten sich niemand verlassen kann. Aenderungen wissen nicht, in welchem Kontext sie aufgerufen wurden (Adresse aendern? IBAN? Gehalt? Vertrag beenden?); der Kontext, den das UI evtl. kannte, ist verloren. Es gibt keine Moeglichkeit fuer kontextabhaengige Logik oder Nachbedingungen. Fachliche Regeln landen daher in der Applikationsschicht (gehoert nicht dorthin) oder im UI (bei mehreren UIs redundant; und jeder Entwickler koennte Employee beliebig manipulieren – z.B. sich selbst eine Gehaltserhoehung geben).
5. Strukturbild des Antipatterns
Im Antipattern gilt fuer die Schichten: Der Controller (HTTP Inbound Adapter) enthaelt keine Geschaeftsregeln (was richtig ist). Die Anwendungsschicht (Application/Inbound Port) koennte die Geschaeftsregeln implementieren, macht es aber nicht (was richtig ist). Service und Repository enthalten ebenfalls keine Geschaeftslogik. Die Entity Employee hat Daten, aber kein Verhalten – Verstoss gegen die OO-Prinzipien Kapselung und Invariantenerhaltung; sie modelliert nur ein statisches Datenmodell. Ergebnis: Die Anwendung kann nur CRUD, aber nicht mehr.
6. Missverstaendnisse, die das Antipattern rechtfertigen
Vier Ursachen fuer die Verbreitung: (1) Framework-Tutorials arbeiten fast immer mit einem trivialen, blutleeren Domaenenmodell, weil sie das Framework erklaeren, nicht ein Domaenenproblem loesen sollen. (2) Missverstandenes SRP – eine Klasse soll nur einen Grund zur Aenderung haben; das heisst NICHT, Daten und Verhalten als getrennte Verantwortlichkeiten zu sehen. Deren Trennung ist prozedurales Programmieren; das OO-Prinzip der Kapselung gilt. (3) Daten(bank) oder Benutzerschnittstelle zuerst – scheinbar einfach zu verstehen, man erntet die low hanging fruits statt sich mit der Domaenenkomplexitaet zu befassen. (4) Denken in Technologien oder Daten statt in Verhalten – IT-Experten loesen gerne mit Technologien (REST/Microservice/JEE/Cassandra), Fachexperten sehen die Daten als am greifbarsten und uebersehen die Komplexitaet ihres gelebten Verhaltens.
7. Domain Driven Design – Grundidee und Herkunft
DDD ist die Antwort auf das Antipattern. Quelle: Eric Evans, Domain Driven Design – Tackling Complexity in the Heart of Software (2004). DDD gliedert sich in eine Strategische Ebene (Kontext des fachlichen Problems klaeren – Bedeutung gibt es nur in einem Kontext; Wissen ueber das Problem sammeln, tiefes Verstaendnis anstreben; Allgegenwaertige Sprache: Begriffe definieren und Regeln formulieren) und eine Taktische Ebene (ein reichhaltiges Modell des fachlichen Problems erstellen und dieses Modell so im Code ausdruecken, dass sich die Allgegenwaertige Sprache im Code wiederfindet). Grundhaltung: Domaene zuerst!
8. Strategische Ebene: Domaene, Subdomaene, Kontext
Die Domaene ist per Definition alles, was wir in einer Organisation vorfinden. Eine Domaene besteht aus Subdomaenen, die jeweils einen Kontext definieren – das gilt selbst fuer kleine Organisationen. Begriffe und Regeln haben nur in einem Kontext eine Bedeutung. Kontexte haben Grenzen: Man sucht die Grenzen des Kontexts, macht sie explizit (was ist drin, was ist draussen), vereinheitlicht Begriffe und Regeln NICHT ueber Kontextgrenzen hinweg, und macht ggf. an den Schnittstellen eine Uebersetzung/Abbildung.
9. Beispiel Mitarbeiter in HR vs. Controlling
Durchgaengiges Beispiel Firma Sonnenschein mit Subdomaenen HR und Controlling – beide kennen einen Mitarbeiter, aber unterschiedlich. Gueltigkeit: In HR sind es alle Personen, die einen Arbeitsvertrag mit Sonnenschein haben oder hatten; im Controlling alle Personen, die fuer Sonnenschein arbeiten (also auch externe Mitarbeiter!). Lebenszyklus: HR hat einen komplexen Lebenszyklus, im Controlling reichen drei Zustaende – Aktiv, Inaktiv, In Rente – weil dort nur zaehlt, ob Personalkosten anfallen und ob Bestellungen ausgeloest werden koennen. Konsequenz: Die Schnittstelle zwischen HR und Controlling muss Uebersetzungsarbeit leisten; ein unternehmenseinheitliches Modell fuer Mitarbeiter macht keinen Sinn.
10. Allgegenwaertige Sprache (Ubiquitous Language)
In einem Projektteam bildet sich mit der Zeit eine Allgegenwaertige Sprache aus. Diese und der Code sind meistens alles, was langlebig gueltig bleibt (nicht: die Dokumentation). Deshalb muss der Sprachgebrauch gehegt und gepflegt werden – im Team, im Code und in Dokumenten. Klaerung von Begriffen und sprachliche Formulierung von Regeln ist Softwareengineering. KI-Bezug: Kuenstliche Intelligenz braucht Kontextwissen (oft in einer Datei, die die KI mitschreibt und Menschen pruefen) und fachliche Vorgaben (Regeln), alles in Sprache formuliert. Kontextwissen, Regeln, Prompts, Namensgebungen und Kommentare im Code muessen dieselbe Sprache sprechen; unklare Sprache fuehrt zu KI-Halluzinationen.
11. Taktische Ebene: die drei Schichten/Hexagons
Die Taktische Ebene liefert konkrete Modellierungsmuster. Der Anwendungskern wird in drei Schichten/Hexagons aufgeteilt: Domaene, Applikation, Infrastruktur. Dieses Konzept ist Teil von DDD und wurde bereits in der logischen Sicht besprochen. Zentrale Frage der taktischen Ebene: Wie bauen wir die Domaenenschicht?
12. Baukasten der Domaene: Entitaeten und Wertobjekte
Zur OO-Modellierung des Domaenenmodells dienen Entitaeten (entities) und Wertobjekte (value objects). Entitaeten haben eine Identitaet und einen Lebenszyklus; Wertobjekte modellieren fachliche Konzepte ohne eigene Identitaet (Gegenmittel gegen das Problem, dass im Antipattern Employee nur Java-Standardfelder statt modellierter Konzepte hatte). Diese Objekte tragen fachliches Verhalten und garantieren ihre Invarianten – im Gegensatz zur blutleeren Datenstruktur.
13. Baukasten der Domaene: Domain Events, Domain Services, Aggregate
Domain Events erlauben Kommunikation zwischen Entitaeten und zum Applikationshexagon bei loser Kopplung. Domain Services bieten fachlicher Logik, die nicht sinnvoll in eine Entitaet oder ein Wertobjekt passt, eine Heimat. Aggregate sind Gruppierungen von Entitaeten und Wertobjekten; sie definieren die Granularitaet von Transaktionen und Sperren und balancieren damit Performanz und Skalierbarkeit gegen Konsistenz.
14. Applikation und Infrastruktur: Ports und Adapter
Fuer die Struktur von Applikation und Infrastruktur liefert DDD selbst keine Konzepte. Diese Luecke wird durch das Hexagonale Modell mit Ports und Adaptern geschlossen. Die Referenzarchitektur (logische Sicht) je Komponente: Domain (*.domain) mit Domain Service und Domain Event; darum die Application (*.application) mit Inbound Port und Outbound Port; aussen die Infrastructure (*.infra) mit Inbound Adapter und Outbound Adapter. Die Codeabhaengigkeit zeigt von aussen nach innen zur Domaene.
15. Referenzarchitektur einer Unternehmensanwendung
Die Zusammenfassung zeigt die Referenzarchitektur der logischen Sicht mit mehreren Komponenten (Komponente A – rootpackage.a, Komponente B – rootpackage.b). Jede Komponente ist gleich aufgebaut: innen Domain (*.domain), darum Application (*.application) mit Inbound/Outbound Ports, aussen Infrastructure (*.infra) mit Inbound/Outbound Adaptern. Package-Konvention macht die Schichtzugehoerigkeit sichtbar (*.domain, *.application, *.infra). Die Domaene enthaelt Domain Service und Domain Event.
Merksätze
Klausur-Stichworte (aus der Zusammenfassung)
Theißen hat gesagt: alle prüfbaren Themen tauchen als Stichwort in den Zusammenfassungs-Folien auf. Genau diese Stichworte stehen hier.
- Antipattern Blutleere Domaene: Domaenenschicht mit Platz fuer Daten, aber ohne fachliches Verhalten
- Blutleere Domaene fuehrt dazu, dass alle Verantwortung fuer fachliche Regeln an die Clients delegiert wird
- Prinzipien DDD: Kontext des fachlichen Problems klaeren – Bedeutung gibt es nur in einem Kontext
- Prinzipien DDD: Wissen ueber das fachliche Problem sammeln, tiefes Verstaendnis anstreben
- Prinzipien DDD: Allgegenwaertige Sprache – Begriffe definieren und Regeln formulieren
- Prinzipien DDD: Modellieren – ein reichhaltiges Modell des fachlichen Problems erstellen
- Prinzipien DDD: dieses Modell im Code ausdruecken, sodass sich die Allgegenwaertige Sprache im Code wiederfindet
- Prinzipien DDD: Domaene zuerst! Steuerung, Persistenz, UI kommen spaeter
- Strategische Ebene: Domaene in Subdomaenen aufteilen, Raender der Subdomaenen identifizieren
- Strategische Ebene: jede Subdomaene definiert einen Kontext, in dem Begriffe und Modelle Gueltigkeit haben
- Strategische Ebene: ggf. zwischen Kontexten uebersetzen
- Strategische Ebene: Allgegenwaertige Sprache pro Kontext
- Taktische Ebene: Aufteilung des Codes des Anwendungskerns in Infrastruktur, Applikation, Domaene
- Taktische Ebene: Baukasten fuer die Domaene – Entitaeten, Wertobjekte, Domain Service, Domain Event, Aggregate
- Referenzarchitektur (logische Sicht) einer Unternehmensanwendung mit Codeabhaengigkeit nach innen; Packages *.infra, *.application, *.domain
Übungsfragen
F1 Was versteht man unter dem Antipattern Blutleere Domaene (anemic domain layer)?
Eine Domaenenschicht, die zwar Platz fuer Daten hat, aber kein fachliches Verhalten. Die Domaenenobjekte sind reine, statische Datenstrukturen ohne Regeln oder Invarianten. Folge ist, dass die gesamte Verantwortung fuer die fachlichen Regeln an die Clients (Applikationsschicht oder UI) delegiert wird und die fachliche Logik keine Heimat im Code hat.
F2 Warum ist eine blutleere Domaene gravierend, obwohl sie technisch funktioniert?
Weil nichts die Invarianten der Geschaeftsobjekte garantiert – niemand kann sich auf die Daten verlassen. Aenderungen kennen ihren fachlichen Kontext nicht (Adresse, IBAN, Gehalt, Vertragsende sind ununterscheidbar), sodass kontextabhaengige Logik und Nachbedingungen fehlen. Fachliche Regeln landen im UI oder in der Applikationsschicht, wo sie nicht hingehoeren, bei mehreren UIs redundant sind und beliebig manipulierbar werden.
F3 Wie wird das Antipattern Blutleere Domaene oft faelschlich mit dem Single Responsibility Principle gerechtfertigt, und warum ist das falsch?
Man liest SRP so, dass eine Klasse nur einen Grund zur Aenderung haben soll, und schliesst daraus, Daten und Verhalten seien getrennte Verantwortlichkeiten. Das ist ein Missverstaendnis: Die Trennung von Daten und Verhalten ist prozedurales Programmieren, waehrend das OO-Prinzip der Kapselung gerade verlangt, Daten und das darauf operierende Verhalten zusammenzuhalten.
F4 Nenne die vier typischen Missverstaendnisse, die zur Verbreitung der blutleeren Domaene fuehren.
1) Framework-Tutorials, die mit trivialen, blutleeren Modellen arbeiten, um das Framework zu erklaeren. 2) Missverstandenes SRP (Trennung von Daten und Verhalten). 3) Daten(bank)- oder UI-first-Denken, das die low hanging fruits erntet statt die Domaenenkomplexitaet anzugehen. 4) Denken in Technologien oder Daten statt in Verhalten.
F5 Was ist Domain Driven Design und von wem stammt es?
DDD ist die Antwort auf das Antipattern der blutleeren Domaene: ein Ansatz, der die fachliche Komplexitaet in den Mittelpunkt der Software stellt und der fachlichen Logik eine Heimat gibt. Es geht auf Eric Evans zurueck, sein Buch Domain Driven Design – Tackling Complexity in the Heart of Software von 2004.
F6 Welche zwei Ebenen unterscheidet DDD und was leistet jede?
Die Strategische Ebene klaert den Kontext des fachlichen Problems, sammelt Wissen zum tiefen Verstaendnis und etabliert die Allgegenwaertige Sprache (Begriffe definieren, Regeln formulieren). Die Taktische Ebene erstellt ein reichhaltiges Modell des fachlichen Problems und drueckt dieses so im Code aus, dass sich die Allgegenwaertige Sprache im Code wiederfindet. Leitprinzip beider: Domaene zuerst.
F7 Was bedeutet der Grundsatz Domaene zuerst?
Zuerst wird das fachliche Domaenenmodell gebaut; Steuerung/Ablauf, Benutzerschnittstelle und Datenbank sind Details, um die man sich erst spaeter kuemmert. Man soll sich also nicht von Technologie oder Daten leiten lassen, sondern vom fachlichen Verhalten.
F8 Erklaere den Zusammenhang von Domaene, Subdomaene und Kontext in der strategischen Ebene.
Die Domaene ist per Definition alles, was in einer Organisation vorgefunden wird. Sie besteht aus Subdomaenen, die jeweils einen Kontext definieren – das gilt sogar fuer kleine Organisationen. Begriffe und Regeln haben nur innerhalb eines Kontexts Bedeutung; die Kontextgrenzen muessen gesucht und explizit gemacht werden.
F9 Warum ist ein unternehmensweit einheitliches Modell fuer den Mitarbeiter laut dem Sonnenschein-Beispiel nicht sinnvoll?
Weil derselbe Begriff Mitarbeiter in verschiedenen Subdomaenen unterschiedliche Bedeutung hat. In HR sind es alle Personen mit einem Arbeitsvertrag (auch ehemalige), im Controlling alle, die fuer die Firma arbeiten – inklusive externer Mitarbeiter. Auch die Lebenszyklen unterscheiden sich (HR komplex, Controlling nur Aktiv/Inaktiv/In Rente). Deshalb muss an der Schnittstelle uebersetzt statt vereinheitlicht werden.
F10 Was ist die Allgegenwaertige Sprache und warum ist ihre Pflege Teil des Softwareengineerings?
Die Allgegenwaertige Sprache ist der gemeinsame Begriffs- und Regelsatz, der sich im Projektteam ausbildet. Sie und der Code sind meist das einzig langlebig Gueltige (nicht die Dokumentation), weshalb der Sprachgebrauch im Team, im Code und in Dokumenten gehegt werden muss. Begriffe zu klaeren und Regeln sprachlich zu formulieren ist deshalb selbst Softwareengineering.
F11 Welchen Bezug stellt die Vorlesung zwischen Allgegenwaertiger Sprache und Kuenstlicher Intelligenz her?
KI braucht Kontextwissen (oft in einer Datei, die die KI mitschreibt und Menschen pruefen) und fachliche Vorgaben/Regeln, alles in Sprache formuliert. Kontextwissen, Regeln, Prompts, Namen und Kommentare im Code muessen dieselbe Sprache sprechen; unklare Sprache fuehrt zu KI-Halluzinationen.
F12 In welche drei Schichten/Hexagons wird der Anwendungskern in der taktischen Ebene aufgeteilt?
In Domaene, Applikation und Infrastruktur. Dieses Konzept ist Teil von DDD und wurde bereits in der logischen Sicht behandelt. Die Codeabhaengigkeit zeigt von aussen (Infrastruktur) nach innen zur Domaene.
F13 Aus welchen Bausteinen besteht der Baukasten der Domaene und wozu dienen sie?
Entitaeten und Wertobjekte modellieren das Domaenenmodell objektorientiert. Domain Events erlauben lose gekoppelte Kommunikation zwischen Entitaeten und zum Applikationshexagon. Domain Services geben fachlicher Logik eine Heimat, die nicht in Entitaeten oder Wertobjekte passt. Aggregate gruppieren Entitaeten und Wertobjekte und definieren die Granularitaet von Transaktionen und Sperren.
F14 Was leisten Aggregate und welchen Zielkonflikt balancieren sie?
Aggregate sind Gruppierungen von Entitaeten und Wertobjekten. Sie definieren die Granularitaet von Transaktionen und Sperren und balancieren damit Performanz und Skalierbarkeit auf der einen Seite gegen Konsistenz auf der anderen Seite.
F15 Wozu dienen Domain Services und wann setzt man sie ein?
Domain Services bieten fachlicher Logik eine Heimat, die sich nicht sinnvoll einer einzelnen Entitaet oder einem Wertobjekt zuordnen laesst. Sie sind Teil der Domaenenschicht und tragen fachliches Verhalten, kuemmern sich aber nicht um Ablaufsteuerung oder Technik.
F16 Warum liefert DDD keine Konzepte fuer Applikation und Infrastruktur, und wie wird diese Luecke geschlossen?
DDD fokussiert auf die Domaene und macht zur Struktur von Applikation und Infrastruktur keine Vorgaben. Diese Luecke wird durch das Hexagonale Modell mit Ports und Adaptern geschlossen: Inbound/Outbound Ports in der Applikation und Inbound/Outbound Adapter in der Infrastruktur.
F17 Beschreibe den Aufbau der Referenzarchitektur (logische Sicht) einer Komponente und die Richtung der Codeabhaengigkeit.
Innen liegt die Domain (*.domain) mit Domain Service und Domain Event, darum die Application (*.application) mit Inbound Port und Outbound Port, aussen die Infrastructure (*.infra) mit Inbound Adapter und Outbound Adapter. Die Codeabhaengigkeit zeigt von aussen nach innen, also zur Domaene hin; die Package-Namen (*.infra, *.application, *.domain) machen die Schichtzugehoerigkeit sichtbar.
F18 Welche Rolle spielte der EmployeeHttpInboundAdapter im NGHR-Durchstich und was war daran problematisch?
Er ist die HTTP-Schnittstellenklasse mit Methoden, die spaeter an HTTP-Requests gebunden werden sollen. Dass die HTTP-Anbindung noch fehlt, war kein Befund. Problematisch war jedoch, dass er das Geschaeftsobjekt Employee nach aussen exponiert und keine Interface-Trennung (ISP) fuer HR-Client und Self-Service-Client bietet.