Domain-Driven Design: Abgrenzung zu anderen Methoden

15. März 2013

Im vorherigen Beitrag habe ich die Methode des Domain-Driven Designs [1] vorgestellt, die das Domain Model als zentrale Architektur in den Vordergrund stellt. Aufgrund der Konzentration auf die eigentliche Businesslogik, die im Kern der Software gekapselt ist, können mit dieser Methode entwickelte, komplexe Softwaresysteme flexibel an etwaige Änderungen dieser Logik angepasst werden.

Das Domain Model ist allerdings nicht die einzig existierende Architektur. In der Praxis werden zunehmend Designs eingesetzt, die die Vorteile objektorientierter Programmiersprachen vernachlässigen und stattdessen zunehmend prozeduralen Charakter haben. Ein gutes Beispiel für eine solche Architektur ist die übermäßige Verwendung des Transaction Script Pattern, bei dem sämtliche Businesslogik in einen separaten Business Layer verlagert wird, während Entities und Value Objects nur noch als reine Datencontainer fungieren. Das Resultat wird von Fowler auch als anämisches Domain Model bezeichnet [2]. Dieser Ansatz hat eine Reihe von Nachteilen:

  1. Es ist nicht sofort ersichtlich, welche Daten von welcher Fachlogik verwendet werden, höchstens vielleicht über die Parameter der Servicemethode, deren Namen oder den der Klasse/Schnittstelle, in der sie definiert ist.
  2. Häufig wird eine Kette von Servicemethoden nacheinander aufgerufen, meist sogar über weitere interne Abstraktionsschichten hinweg. Dies macht die Fehlersuche schwierig und reduziert die Nachvollziehbarkeit der Fachlogik sowie deren Refactoring. Abhängigkeiten zwischen verschiedenen Servicemethoden sind schwierig zu detektieren, es entstehen oft komplexe Abhängigkeitsgraphen.
  3. Datenobjekte haben keine “Intelligenz”, d. h. sie verwalten nicht einmal ihren eigenen Zustand. Eigenschaften sind meist les- und schreibbar zugleich, denn der Objektzustand wird von außen gesteuert. Dem Entwickler ist daher aus Design oder Schnittstellen des Datenobjekts (Eigenschaften, Methoden) nicht explizit ersichtlich, wie eine Instanz korrekt zu initialisieren ist. Dies macht weitgehende Konsistenzprüfungen in allen Servicemethoden notwendig, die die Instanz verwenden, um zu verhindern, dass Instanzen mit inkonsistentem Zustand verwendet werden. Auch hier gestaltet sich eine Fehlersuche schwierig.

Um Missverständnissen vorzubeugen, wiederhole ich an dieser Stelle meinen Disclaimer: Die im vorherigen Absatz erwähnten Architekturen sind nicht per se besser oder schlechter als das Domain Model, sondern können im konkreten Fall geeignet oder weniger geeignet sein, um die Fachanforderungen abzubilden. Ein “One Architecture Fits All”-Ansatz ist nicht zielführend und wird auch hier nicht vertreten.

Nichtsdestotrotz existieren eine Reihe von Vorurteilen gegen das Domain Model, die teilweise auf falschen Vorstellungen über diesen Architekturansatz beruhen, und die oft als Argument gegen dessen Verwendung vorgebracht werden. Typische Fragen, die in diesem Zusammenhang häufig aufkommen, habe ich hier beispielhaft beantwortet:

Wenn Entities Fachlogik enthalten, wie kann ich sie dann über Schichten hinweg verteilen?

Gar nicht. Das Domain Model, und somit alle Entities, werden nur im Kern der Software verwendet. Sie verlassen den Kern, der normalerweise aus Domain-, Infrastructure- und Application-Layer besteht (wobei hier die Grenze gezogen wird), nicht. Müssen Daten mit anderen Schichten oder Komponenten ausgetauscht werden, so kommen grundsätzlich DTOs (Data Transfer Objects) zum Einsatz [3]. Diese enthalten nur Daten und keine Fachlogik. Zugriff auf die Fachlogik erfolgt über den Application Layer, der Anfragen wiederum an die entsprechenden Entities im Domain Layer delegiert.

Heißt das, dass ich Daten jedes Mal mappen muss?

Ja, aber das ist auch bei der Benutzung anderer Methoden notwendig. Bei der Verwendung von Daten im MVC-Pattern auf dem Presentation Layer zum Beispiel werden in Model-Klassen die auf der View darzustellenden Daten abgebildet. Diese entsprechen aber nicht unbedingt den Datenobjekten im Anwendungskern und müssten deshalb ohnehin gemappt werden. Als Faustregel kann gelten, dass Daten zwischen den Schichten außerhalb des Anwendungskerns über DTOs ausgetauscht werden. Dies gilt folglich nicht für Domain-, Application- und Infrastructure-Layer.

Bedeutet das, dass Entities Persistenzlogik enthalten?

Nein. Die Persistierung wird durch den Infrastructure Layer abgedeckt. Hier kommen beispielsweise O/R-Mapping-Tools wie NHibernate zum Einsatz. Persistenzlogik ist streng genommen keine Fachlogik und sollte deshalb auch nicht im Verantwortungsbereich einer Entity liegen.

Wie verhält es sich mit der Testbarkeit von Domain Models?

Grundsätzlich nicht anders als bei anderen Ansätzen, wobei ein zusätzliches Augenmerk auf den Zustand der Entities zu legen ist. Grundsätzlich verwalten Entities (oder Aggregates) ihren Zustand selbst, z. B. über die Steuerung des Zugriffs auf ihre Attribute (bzw. interne Strukturen), d. h. viele Properties erlauben beispielsweise ausschließlich lesenden Zugriff. Oft müssen Entities auch über einen parameterbehafteten Konstruktor erzeugt werden, um einen validen Anfangszustand zu schaffen, während bei komplexeren Entities und Aggregaten Factories für diesen Zweck benutzt werden. Zu beachten ist, dass Abhängigkeiten zwischen Modulen des Domain Models und Komponenten von Drittanbietern auch (und gerade) bei DDD grundsätzlich über DI und Entwurfsmuster wie beispielsweise das Domain Event Pattern [4, 5] vermieden werden, d. h. das Testen eines Domain Models ist hier nicht schwieriger bzw. aufwendiger als das eines anämischen Domain Models.

Ist DDD nicht mit erheblichem Mehraufwand verbunden?

Aufwand ist relativ. Domain Models sind außerordentlich gut geeignet, um komplexe Fachzusammenhänge abzubilden und auf Änderungen flexibel reagieren zu können [1]. Dies liegt daran, dass Fachlogik grundsätzlich in der Entity (oder dem Aggregat) angesiedelt ist, zu dem sie gehört. Das Modell dient vorwiegend der Abbildung von Architektur- und Designentscheidungen. Diese sollten auch Bestandteil anderer Entwicklungsmethoden sein. Langfristig gesehen senkt die Verwendung eines Domain Models im Kontext komplexer Businessanwendungen die Wartungskosten. Da es modellgetrieben entwickelt wird, können viele Codeartefakte zudem automatisch generiert werden.

Der Aufwand ist überdies auch in hohem Maße vom Detailgrad des Modells abhängig. Hier gilt die Devise “so viel wie nötig”; ein Overhead ist zu vermeiden. Es geht nicht darum, ein feingranulares, hochdetailliertes Modell zu entwickeln, sondern dieser Prozess findet iterativ statt: zunächst abstrakt, später detailliert. Ich persönlich beschränke mich hierbei auf wichtige Architektur- und Designentscheidungen, bei denen eine Modellierung einen echten Mehrwert für das Projekt und die Diskussion mit dem Entwicklerteam mit sich bringt.

Benötige ich für DDD ein Modellierungstool?

Nicht unbedingt, aber ein Tool ist von großem Vorteil, denn der Entwicklungsprozess ist bereits in den frühen Phasen modellgetrieben. Das Modell lässt sich theoretisch auch ohne Tool erstellen und warten, allerdings wird dies dadurch erheblich erschwert. Das Tool sollte die gewählte Modellsprache unterstützen und in der Lage sein, Code zu generieren sowie existierenden Code im Rahmen des Reverse Engineerings in ein bestehendes Modell zu übernehmen. Idealerweise lässt sich dieser Prozess in beide Richtungen über Vorlagen anpassen, so dass projektspezifische Besonderheiten abgebildet werden können.

Wenn mein Projekt vorwiegend datenzentrisch ist, warum sollte ich dann ein Domain Model verwenden?

Viele Softwareprojekte sind Weiterentwicklungen von vorhandenen Systemen. In solchen Fällen und bei detailliert vorliegenden Anforderungen ist oft bereits ein mehr oder weniger gut dokumentiertes Datenmodell vorhanden. Viele Entwicklerteams nähern sich dem Problem daher von dieser Seite aus und verlieren darüber die dynamischen Aspekte und die Fachlogik aus den Augen. Es empfiehlt sich deshalb, zunächst die Anforderungen des Fachbereichs an das gewünschte Verhalten der Software losgelöst von einem eventuell vorhandenen relationalen Datenmodell zu betrachten. Auf der Grundlage dieser Anforderungen sollte das weitere Vorgehen geplant bzw. das Domain Model entwickelt werden. Jedes so erstellte Modell lässt sich ohne übermäßig hohen Aufwand auf bestehende Datenstrukturen abbilden, hierfür existieren geeignete O/R-Mapping-Tools. Stellt man während dieses Prozesses fest, dass die Hauptanforderungen der Fachseite in der Anzeige und dem Speichern von Daten bestehen, so kann die Entscheidung auch für die Verwendung von Transaction Script in Verbindung mit einfachen Datenobjekten fallen.

Ist die Erfahrung der Mitglieder meines Entwicklerteams wichtig?

Ja und nein. In Entwicklerteams, die bisher überwiegend Transaction Script als Pattern eingesetzt haben, erfordert DDD ein Umdenken. Solche Teams werden dieser Methode deshalb zunächst mit Skepsis begegnen. DDD lebt von der aktiven Mitarbeit aller am Prozess beteiligten Akteure, und zwar auf der Grundlage des Modells. Jedes Mitglied des Entwicklungsteams ist für dessen Entwicklung gleichermaßen verantwortlich. Dies erfordert zunächst Überzeugungsarbeit. Evans [1] empfiehlt, bei sehr unerfahrenen Entwicklerteams unter Umständen von DDD Abstand zu nehmen, wenn die Qualität der Software in Gefahr ist Schaden zu nehmen.

Wie passt DDD in den Kontext agiler Softwareentwicklung?

DDD ist Bestandteil agiler Softwareentwicklung und alle agilen Methoden lassen sich problemlos mit DDD kombinieren. Die Grundlage bildet das Modell, das alle wichtigen Aspekte der Software abdeckt. Mit UML zum Beispiel lassen sich alle Phasen des Entwicklungsprozesses modellieren. Das Modell ist allerdings nicht als “heilige Kuh” anzusehen, sondern als ein von allen Entwicklern aktiv gewartetes und iterativ verfeinertes Artefakt. Mit dem geeigneten Modellierungstool kann man während des gesamten Entwicklungsprozesses die getroffenen Designentscheidungen evaluieren, denn Code kann jederzeit generiert und/oder existierender Code in das Modell integriert werden.

Fazit

DDD ist aufgrund seiner konsequenten Fokussierung auf die Fachlogik für die Entwicklung und Wartung komplexer Enterprise-Anwendungen das Mittel der Wahl. Im Zentrum der Anwendung wird die Fachlogik im sogenannten Domain Model abgebildet, welches nach außen über mehrere Schichten abstrahiert wird. Dadurch werden Abhängigkeiten zu anderen Teilsystemen oder externen Frameworks aufgelöst. Die Konzentration auf Fachlogik und dem Modell als gemeinsame Sprache macht allerdings den engen Kontakt des Entwicklerteams mit der Fachseite notwendig.

Ebenso erfordert DDD bei den am Entwicklungsprozess der Software beteiligten Personen ein Umdenken, insbesondere in solchen Teams, in denen bisher vorwiegend andere Entwicklungsmethoden (z. B. anämisches Domain Model) zum Einsatz kamen. Das Vorurteil, dass DDD grundsätzlich mit Mehraufwand verbunden ist, lässt sich bei genauer Betrachtung nicht halten. Im Gegenteil kann die Verwendung von DDD langfristig den Wartungsaufwand senken.

Referenzen

[1] Evans, Eric: Domain-Driven Design – Tackling Complexity in the Heart of Software, Addison-Wesley 2004
[2] Fowler, Martin: Anemic Domain Model, Online-Publikation, http://www.martinfowler.com/bliki/AnemicDomainModel.html, Stand: 4. Februar 2013
[3] Fowler, Martin: Patterns of Enterprise Application Architecture, Addison-Wesley 2003
[4] Fowler, Martin: Domain Event, Online-Publikation, http://www.martinfowler.com/eaaDev/DomainEvent.html, Stand: 4. Februar 2013
[5] Dahan, Udi: Domain Events – Salvation, Online-Publikation, http://www.udidahan.com/2009/06/14/domain-events-salvation/, Stand: 4. Februar 2013