Darstellung einer digitalen hexagonalen Struktur, symbolisiert Datenmanagement in der hexagonalen Architektur.

Von Schichten zu Ringen – Hexagonale Architekturen erklärt

Voraussichtliche Lesedauer: 10 Minuten
HomeRatgeberHexagonale Architektur
Autor: Silas Graffy
Autor: Silas Graffy
Auf unserem diesjährigen Seminar des Bereichs IT-Sanierung habe ich, bewaffnet mit Stift und Flipchart, einen kurzen Talk zu hexagonalen Architekturen gehalten. Und da ich glaube, dass sich dieser Architekturstil viel zu langsam durchsetzt, möchte ich ihn auch hier noch einmal erläutern.

Warum Schichten nicht genug sind …

Aber starten wir bei etwas, das wir alle gut kennen, einer klassischen Schichtenarchitektur. Ein_e Anwender_in interagiert über die UI-Komponente mit der Domänenschicht einer Anwendung, die wiederrum über einen Data Abstraction Layer auf eine Datenbank zugreift.
Diagramm der Klassen in der hexagonalen Architektur, zeigt UI, Domain, DAL und DB in der hexagonalen Architektur.
Zum einen vermittelt eine solche Architektur ein falsches Bild: Die Datenbank als Fundament der Softwarearchitektur – die Zeiten, in denen man einen Softwareentwurf mit einer Entity-Relationship-Modellierung startete, lassen grüßen. Zum anderen besteht eine Abhängigkeit vom fachlichen Code des Domain-Layers zum technischen Code im DAL, wie wir an den Beziehungen der enthaltenen Klassen sehen (natürlich vereinfacht die Abbildung hier stark, mit nur einer Klasse je Schicht):
Diagramm der Klassen in der hexagonalen Architektur, zeigt UI, Domain, DAL und DB in der hexagonalen Architektur.
Diese Abhängigkeit zwingt uns oft, unseren fachlichen Code auch dann anzupassen, wenn sich eigentlich nur technische Infrastruktur geändert hat – und das passiert bei langlebigen Softwaresystemen typischerweise sehr viel häufiger als grundlegende fachliche Änderungen. Aber auch umgekehrt lassen sich eben solche fachlichen Weiterentwicklungen sehr viel einfacher umsetzen, wenn dabei keine externen Abhängigkeiten – zu technischer Infrastruktur oder sonstigem – bestehen.

… und was man dagegen tun kann

Was macht also der kluge Software Crafter oder die kluge Software Crafterin, die Domain und DAL voneinander entkoppeln möchte? Er oder sie extrahiert ein Interface (sowieso eine gute Idee bzgl. Tests und Mocking):
Diagramm zeigt die hexagonale Architektur mit Schnittstellen zwischen UI, Domain, DAL und DB. Hexagonale Architektur.
Nun hängt der fachliche Code im Domain Layer zwar nicht mehr von der technischen Implementierung in der DAL-Klasse ab, auf der Ebene der Komponentenabhängigkeiten hat sich aber (noch) nichts geändert. Abhilfe schafft hier das Dependency Inversion Principle (DIP; Robert C. Martin erklärt im als fiktives Streitgespräch gehaltenen Blog-Artikel A Little Architecture sehr schön dessen Auswirkung auf Softwarearchitekturen):
Diagramm zeigt die hexagonale Architektur mit Interaktionen zwischen User, Domain, DB, Log Files und Cloud API Consumer. Hexagonale Architektur.
Wichtig bei diesem Schritt der Abhängigkeitsumkehr ist es, darauf zu achten, das Interface nicht bloß zu verschieben, sondern es wirklich zum Teil des Domain Models zu machen, z. B. indem Methoden fachlich statt technisch benannt werden. In jedem Fall ergeben sich nun die folgenden Komponentenabhängigkeiten:
Darstellung der Klassen mit Schnittstellen zwischen UI, Domain, DAL und DB in der hexagonalen Architektur.

Innen und außen statt oben und unten

Nun haben die wenigsten Softwaresysteme keine weiteren Schnittstellen außer User Interface und Datenbank (als Persistenzmedium). Oft gibt es zusätzlich noch APIs, um die Funktionalität der Software anderen Systemen zur Verfügung zu stellen (z. B. mittels REST Gateway), Logging in Dateien oder einen anderen Speicher, ggf. auch E-Mail-Benachrichtigung bei bestimmten Ereignissen, und und und … Ordnet man den hierfür nötigen Code – wo nötig unter Berücksichtigung des DIP – ebenfalls in Schichten um den Domain Layer an, ergibt sich eine Architektur ähnlich dieser:
Diagramm der hexagonalen Architektur zeigt User, Cloud API Consumer, UI, Domain, Log Files, Mail und DB. Hexagonale Architektur.
Alistair Cockburn begann Mitte der 1990er Jahre damit, diese Art der Architektur mit einem Hexagon zu visualisieren:
Erklärendes Diagramm zeigt Klassen und Schnittstellen innerhalb der hexagonalen Architektur: UI, Domain, DAL und DB. Hexagonale Architektur.
Da die sechs Seiten mehr oder weniger willkürlich erschienen, erfolgte 2005 die Umbenennung zu Ports and Adapters. In dieser Nomenklatur stehen die ins Innere der Anwendung (Domain) gewanderten Interfaces für die Ports, während deren Implementierungen im äußeren Sechseck eine Adapterfunktion zwischen Anwendungskern und Benutzer_innen, Datenbanken, Logdateien, Fremdsystemen etc. zukommt.

Schichten 2.0

Trennt man sich nun von der Darstellung als Sechseck und unterscheidet gleichzeitig etwas feiner zwischen Innen und Außen, ergibt sich die 2008 von Jeffrey Palermo vorgeschlagene Onion-Architektur.
Schema zeigt Interaktionen zwischen REST GW, UI, Domain, Logger, DAL und DB in der hexagonalen Architektur.

Sie startet im Inneren mit einem Domain Model, das frei von allen Abhängigkeiten die fachlichen Bausteine der Anwendung beschreibt. Der Domain Services Ring darum beinhaltet fachliche Logik, die sich über mehrere Elemente des Domänenmodells erstreckt. Er ist ausschließlich vom Domain Model abhängig und bildet gemeinsam mit diesem die gesamte Fachlichkeit ab. Diese wird genutzt von den Application Services, die anwendungsspezifische Logik wie z. B. Rechtesteuerung, implementieren. Schließlich liegen Infrastructure, User Interfaces und APIs und auch Tests in einer Schicht darum.

Abhängigkeiten verlaufen dabei immer von Außen nach Innen und niemals anders herum. Das betrifft neben Code-Abhängigkeiten natürlich auch Datenformate. Beispielsweise sollte eine Zeichenkette, deren Format von einem externen System definiert wird, niemals in einer Schicht weiter innen als Infrastructure interpretiert werden. Stattdessen definieren die inneren Schichten Formate – oder besser eigene Datentypen –, auf die die Zeichenkette durch den Adapter in der Infrastrukturschicht abgebildet wird.

Robert C. Martins Clean Architecture von 2012 kann als Derivat der Onion-Architektur mit abweichenden Bezeichnern betrachtet werden.

Als Vorteile dieses Architekturmuster lassen sich zusammenfassen:

  • Die Fachlogik kann unabhängig von Infrastruktur kompiliert, deployed und wiederverwendet werden.
  • Die Anwendungslogik kann von verschiedenen UIs, Batches, Daemons/Diensten und Tests gleichermaßen genutzt werden.
  • Der Anwendungskern bleibt unabhängig von der Außenwelt.
  • Der Austausch von Persistenzmechanismen je nach Deploymentszenario ist problemlos möglich.
  • Fachlicher und technischer Code sind konsequent voneinander getrennt.

Microservices, Self-contained Systems und Domain-Driven Design

Auch zu Microservices und Self-contained Systems (SCS) erscheint die Onion-Architektur als natural fit. Jeder Service bzw. jedes SCS ist in Form seiner eigenen Zwiebel implementiert. Aus Sicht eines Systems sind alle anderen Systeme Teile der Außenwelt und dürfen als solche keine Abhängigkeiten der inneren Systemschichten darstellen.

Im Domain-Driven Design (DDD) implementiert jede Zwiebel einen Bounded Context, jeweils mit eigener Ubiquitous Language. Eine Context Map beschreibt ihre Beziehungen. Je nach Beziehungstyp nötiger Übersetzungscode lässt sich, z. B. in Form eines Anticorruption Layer, als eigene Zwiebelschicht implementieren, denn die o. g. Schichten dürfen selbstverständlich bedarfsweise erweitert werden. Mit der „Entities“ (nicht zu verwechseln mit Entities im DDD) genannten firmenweiten Fachlogik beschreibt Robert C. Martin in seiner Clean Architecture die Umsetzung des DDD-Konzepts Shared Kernel. Die Application und Domain Services-Schichten beinhalten Elemente, die auch im DDD Services und Repositories genannt werden, Domain Model enthält DDD-Entities, Value Objects und Aggregate Roots.

Fazit

Der beschriebene Architekturstil, der je nach Quelle mal Hexagonal, Ports and Adapters, Onion oder Clean Architecture genannt wird, ist eine konsequente Weiterentwicklung von Schichtenarchitekturen durch Dependency Inversion. So bleibt fachlicher Code im Anwendungskern unabhängig von technischem Code in UIs, Tests, Infrastruktur etc.

Genau wie Domain-Driven Design bieten sich hexagonale Architekturen immer dann an, wenn langlebige Softwaresysteme mit aufwendiger Fachlichkeit entworfen und nachhaltig realisiert werden sollen. Für Wegwerfsoftware, Prototypen, einfache CRUD-Systeme und andere Anwendungsfälle, in denen oft Rapid Application Development zum Einsatz kommt, erscheint der erforderliche Architekturinvest jedoch nicht angemessen.

Finde was zu dir passt