DDD: Ports und Adapter

Vaughn Vernon bespricht in seinem Buch Implementing Domain Driven Design verschiedene Arten, wie ein System intern geschichtet werden kann. Sein favorisiertes Muster ist ‘Ports und Adapter’ oder – wie es an der Quelle des Ansatzes dem Artikel von Alistair Cockburn heisst – Hexagonale Architektur. Die Begrifflichkeit, sowohl Ports und Adapter als auch Heaxagonale Architektur, verwirrte uns in der DDD-Lesegruppe zunächst. Was ist ein Port, was ein Adapter? Wo ist der ApplikationsService verortet? Mir half ein kleines Video von Martin Fowler. Er unterscheidet dort Hexagonale Architektur (Ports und Adapter) von der klassischen 3-Schicht-Architektur. Er visualisiert die Schichten durch Bücher. Die UI-Schicht ist das Buch JavaScript: The Good Parts, die Domänenschicht ist Domain Driven Design und die Persistenzschicht ist The Practical SQL-Handbook. In einer 3-Schicht-Architektur könne, so Fowler, jede Schicht alle Schichten unter sich sehen. Das wird durch die drei Bücher, die aufeinander liegen – JS, DDD, SQL -, visualisiert. Die Schichten sind jeweils von denen, die unter ihnen sind, abhängig. Umgekehrt kenne ein Schicht nichts von denen über sich. So ist die Domänenschicht unabhängig vom User-Interface und die Persistenz ist unabhängig von User-Interface und Domänenschicht. Wesentlich für das Ports und Adapter-Muster ist die Beziehung von Domänenschicht zur Persistenzschicht. Das Muster, in dem die Domänenschicht die Persistenz kenne und von dieser abhängig sei (klassische 3-Schicht-Architektur), sei ActiveRecord. (Fowler hat diesen Begriff in seinem Buch Patterns of Application Architecture vorgestellt. Das gleichnamige Rails-Framework ist die prominenteste Implementierung dieses Musters.) Wenn ich meine Domänenschicht aus dieser Abhängigkeit befreien möchte, wenn ich die Domänenschicht unabhängig von der Persistenz entwickeln und damit auch testen möchte, dann sei das das Pattern DataMapper (‘Patterns of Application Architecture’). Fowler bemüht im Video das Bild einer indischen Gottheit mit 6 Armen. Diese hält die Bücher. In der Mitte DDD. JS und SQL kreisen um diese Mitte. Die Modellierung der Domäne wäre unabhängig von der Persistenz. Die Aufgabe der Persistierung der Objekte übernimmt bspw. ein OR-Mapper (DataMapper-Pattern). Dieser residiere in der Persistenzschicht und wäre in der Domänenschicht gänzlich unbekannt.

Lange Rede kurzer Sinn: Um festzustellen, ob es sich bei der grundsätzlichen Aufteilung eines Systems eher um Ports und Adapter (Hexagonale Architektur) oder um eine klassische 3-Schicht-Architektur handelt, genügt es, die Frage zu beantworten: Ist die Domänenschicht abhängig von der Persistenzschicht oder nicht.

Das technische Mittel um diese Entkopplung zu bewirken ist Dependency Inversion. Die Domänenschicht definiert Interfaces für Repositories, die die fachliche Frage beantworten, mithilfe welcher Parameter möchte ich welche Objekte auffinden. Die Persistenzschicht implementiert diese Interfaces mit einer konkreten Persistenzmechanik wie z.B. Hibernate. Durch Muster wie Registry oder Dependency Injection Container kommen diese Implementierungen ins Spiel.

Kehren wir zum Ausgangspunkt zurück. Wenn UI und Persistenz um die Domäne kreisen, ähneln sie sich. Das ist der Kerngedanke von Cockburns Ansatz. Es gibt eine Domänenschicht, die von nichts abhängig ist. Über Ports und Adapter – Ports sind die Löcher in der Schale, Adapter die Anschluss-Implementierungen, die Loch und Lochprotokoll mit der Applikation verbinden (bspw. das HTTP-Loch, das durch einen Webadapter mit der Applikation verbunden wird). UI und Persistenz sind Verbindungen mit der Außernwelt. Die Symmetrie zwischen UI und Persistenz ist der zentrale Gedanke des Ansatzes. Die Persistenz ist genau so entkoppelt von der Appliktation wie in der klassischen 3-Schicht-Architektur das User-Interface. So wie man für Tests ein anderes Interface (programmatisch JUnit oder ein Kommandozeileninterface) wählen kann, lässt sich auch die Persistenz ersetzen, ohne die Funktion der Applikation zu beeinträchtigen. Im Test nutzt man eine Testdatenbank oder einen Imspeicherdatenbankmock und die Applikation ist leichter zu testen.

So intuitiv das Pattern zunächst scheint, so symmetrisch die kreisenden Bücher wirken, ganz verschwindet die Asymmetrie, der Unterschied von UI und Persistenz nicht. Cockburn spricht von links (UI) und rechts (Persistenz), von treibendenund getriebenen Ports. Das UI nutzt die Klassen der Applikationsschicht direkt, die Klassen der Persistenz kommen indirekt ins Spiel, indem sie die Interfaces der Domäne implementieren und in die Applikation injiziert werden. Hier liegt wahrscheinlich das Problem des Musters. Es führt eine Symmetrie ein: ‘Sind wir, die nicht zur Domänen gehören, nicht alle Ports und Adapter, die um die Domäne kreisen’, der sich die konkrete Erfahrung permanent verweigert. Das Aktiv und Passiv, das Getrieben und Treibend, das Links und Rechts ist für die Arbeit in einer Schicht so wichtig, dass das Gefühl der Ähnlichkeit von UI und Persistenz sich nicht einstellen mag, auch nicht in einem Hexagon.

Mir reicht es den Unterschied von Ports und Adaptern zu einer klassischen 3-Schicht-Architektur auf den von ActiveRecord und DataMapper zu reduzieren. Die Frage nach der Beziehung zwischen Persistenz und Domäne dient mir als Lackmustest. Cockburns Symmetriesierung ist vielleicht anregend, langfristig scheint sie mir nicht instruktiv.

Nachtrag (10.8.2014) Vaughn Vernon hat seine Beispielprojekte so gegliedert, dass es auf der obersten Ebene (unterhalb des obligatorischen Namespaces für das ganze Projekt) drei Pakete gibt: port.adapter, application und domain.model – das Hexagon von außen nach innen. (Er nutzt die Doppelnamen mit Punkt, um die Bezeichnung sprechender zu machen, in domain und port findet sich nichts bis auf ihre Unterpakete.) In port.adapter gibt es dann Unterpakete nach Schnittstellen: messaging, persistence und service.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>