Hexagonal Architecture
Fachliche Regeln von Web, Datenbank und Nachrichten trennen
Die Idee und ihr Nutzen
Eine Versicherung nimmt Schadenmeldungen im Kundenportal, über Makler und aus importierten Dateien entgegen. In allen drei Fällen soll dieselbe Regel gelten: Eine Meldung ohne Vertragsnummer oder Schadendatum wird abgewiesen. Die Prüfung steht im Anwendungskern, einem Programmteil, der weder Webformular noch Datenbank kennt. Für jeden Zugang gibt es eigenen Code, einen Adapter; der Adapter des Kundenportals liest zum Beispiel die Werte aus dem Webformular. Jeder Adapter übergibt die Meldung über denselben vereinbarten Aufruf „Schaden melden“ an den Anwendungskern, und ein solcher vereinbarter Aufruf heißt Port. So wird die Regel nur im Anwendungskern gepflegt. Diese Bauweise heißt Hexagonal Architecture.
Wie funktioniert es?
Eine Kundin trägt im Kundenportal die Vertragsnummer 88123, das Schadendatum 3. Mai und die Beschreibung „Wasserschaden Küche“ ein. Der Portaladapter liest die Werte aus dem Webformular und übergibt sie mit dem vereinbarten Aufruf „Schaden melden“ an den Anwendungskern. Der Anwendungskern prüft, ob Vertragsnummer und Schadendatum vorhanden sind. Beide Angaben sind vorhanden, also ruft der Anwendungskern „Schaden speichern“ auf. Der Datenbankadapter nimmt diesen Aufruf entgegen, speichert die Meldung mit den passenden Datenbankbefehlen in der Schadendatenbank und meldet „gespeichert“ zurück. Erst danach ruft der Anwendungskern „Eingang bestätigen“ auf, und der Mailadapter schickt der Kundin die Bestätigung als E-Mail über den Mailserver. Das Portal zeigt der Kundin „Meldung angenommen“. Am selben Tag schickt ein Makler eine Meldung als Nachricht, in der das Schadendatum fehlt. Der Makleradapter liest die Nachricht und ruft denselben Aufruf „Schaden melden“ auf. Der Anwendungskern lehnt die Meldung mit „Schadendatum fehlt“ ab, genau wie bei einer Eingabe im Portal. Der Makleradapter schickt die Ablehnung als Antwortnachricht an den Makler zurück.
Ein Port ist ein solcher vereinbarter Aufruf, zusammen mit den Angaben, die übergeben werden, und dem Ergebnis, das zurückkommt. „Schaden melden“ erwartet die Angaben zur Meldung und liefert „Meldung angenommen“ oder eine Ablehnung wie „Schadendatum fehlt“. Ein Port ist hier keine Webadresse und kein Netzwerkanschluss, sondern ein Aufruf im Programm. Das Team, das die Anwendung entwickelt, legt alle Ports als Teil des Anwendungskerns fest und benennt sie mit fachlichen Begriffen. Der eingehende Port „Schaden melden“ beschreibt, was die Anwendung anbietet: Der Anwendungskern bietet ihn an, und die Adapter der Zugänge rufen ihn auf. Die ausgehenden Ports „Schaden speichern“ und „Eingang bestätigen“ beschreiben, was der Anwendungskern benötigt. Der Anwendungskern ruft sie auf, und der Datenbankadapter und der Mailadapter bieten sie an.
Ein Adapter ist der Programmteil, der einen Port mit einer bestimmten Technik verbindet. Für importierte Dateien gibt es den Importadapter, der die Dateien liest und mit den gelesenen Angaben ebenfalls „Schaden melden“ aufruft. Der Anwendungskern kennt die Angaben und Ergebnisse der Ports, aber keine Webformulare, Datenbankbefehle oder Mailprotokolle. Welcher Adapter die Aufrufe eines ausgehenden Ports entgegennimmt, wird beim Programmstart festgelegt. Im Betrieb ist das der Datenbankadapter, im Test zum Beispiel ein Adapter, der die Meldungen nur im Arbeitsspeicher hält statt in der Datenbank. Der Name Hexagonal Architecture kommt von der üblichen Zeichnung des Anwendungskerns als Sechseck. Die Zahl sechs hat keine Bedeutung; die Bauweise verlangt keine bestimmte Zahl von Ports oder Adaptern.
Scheitert das Speichern, meldet der Datenbankadapter das an den Anwendungskern zurück. Der Anwendungskern ruft dann „Eingang bestätigen“ nicht auf und gibt dem Portaladapter einen Fehler statt „Meldung angenommen“ zurück.
Hexagonal Architecture: drei Eingänge, eine Prüfung im Anwendungskern
Wann ist es sinnvoll und wann nicht?
Eine Hexagonal Architecture lohnt sich
- Dieselben fachlichen Vorgänge werden über mehrere Zugänge genutzt. Ohne Anwendungskern steht die Prüfung der Pflichtangaben zum Beispiel im Code des Kundenportals, wo die Felder des Webformulars ausgelesen werden. Für den Maklerzugang und den Dateiimport muss das Team die Prüfung dann ein zweites und ein drittes Mal programmieren. Mit Anwendungskern rufen alle drei Adapter dieselbe Prüfung über den Port „Schaden melden“ auf.
- Die fachlichen Regeln ändern sich. Wird zum Beispiel zusätzlich der Schadenort Pflicht, ändert das Team die Regel nur im Anwendungskern, und die geänderte Regel gilt für alle drei Zugänge. Ohne Anwendungskern muss jemand alle drei Prüfungen finden und gleich ändern. Übersieht er eine, gelten je nach Zugang unterschiedliche Regeln, und über den Dateiimport gelangen Meldungen ohne Schadenort ins System.
- Fachliche Entscheidungen sollen ohne Webserver, Datenbank und Mailserver getestet werden. Ruft die Prüfung selbst die Datenbank auf, braucht jeder Test der Regel „Schadendatum muss vorhanden sein“ eine laufende Datenbank mit passenden Testdaten. Solche Tests sind langsam und brechen ab, sobald die Testdatenbank gestört ist. Mit einem Adapter, der die Meldungen im Arbeitsspeicher hält, braucht der Test der Regel keine Datenbank.
- Zugänge, Datenbank und die Art der Bestätigung wechseln im Lauf der Jahre aus technischen Gründen. Die fachlichen Regeln ändern sich dagegen nur, wenn die Fachabteilung es verlangt. Soll die Bestätigung künftig per SMS statt per E-Mail kommen, ersetzt das Team den Mailadapter durch einen SMS-Adapter, der denselben Aufruf „Eingang bestätigen“ anbietet. Der Anwendungskern bleibt unverändert. Ohne Mailadapter stünde der Versand der E-Mail im selben Code wie die fachlichen Regeln, und das Team müsste für die SMS diesen Code ändern und die Regeln erneut testen. Soll die Bestätigung aber zusätzlich die voraussichtliche Bearbeitungsdauer nennen, muss auch der Anwendungskern angepasst werden.
- Ein neuer Zugang ist absehbar. Für einen Anschluss weiterer Partner schreibt das Team nur einen neuen Adapter, der denselben Port „Schaden melden“ aufruft. Ohne Anwendungskern müsste das Team die Prüfung für den neuen Zugang ein viertes Mal programmieren.
Eine Hexagonal Architecture ist nicht das Richtige
- Die Anwendung zeigt nur Daten an und speichert sie, ohne dabei etwas fachlich zu prüfen. Der Anwendungskern hätte dann keine Regel zu prüfen, und jeder Port und jeder Adapter wäre zusätzlicher Code, der die Angaben nur vom Webformular zur Datenbank weiterreicht. Das Team lässt die Oberfläche die Datenbank deshalb direkt aufrufen und spart sich diesen Code.
- Ein Programm formt vor allem Daten von einem Format in ein anderes um und trifft kaum eigene fachliche Entscheidungen. Der Anwendungskern hätte dann fast nichts zu prüfen, und Ports und Adapter würden die Daten nur weiterreichen. Das Team baut die Umformung stattdessen als Folge einzelner Verarbeitungsschritte, etwa mit Pipes and Filters, und spart sich Ports und Adapter.
- Es gibt auf absehbare Zeit genau einen Zugang und eine Datenbank, und es stört nicht, dass die Tests der fachlichen Regeln eine laufende Datenbank brauchen. Die Prüfung steht dann ohnehin nur an einem Ort, und kein Adapter muss ausgetauscht werden. Das Team teilt die Anwendung stattdessen als Layered Architecture in Oberfläche, fachliche Regeln und Datenzugriff. Die Oberfläche ruft die fachlichen Regeln auf, und die fachlichen Regeln rufen den Datenzugriff direkt auf. Ausgehende Ports wie „Schaden speichern“ und die Adapter, die sie anbieten, entfallen.
- Das Team will innerhalb des Anwendungskerns zusätzlich die fachlichen Regeln von den Anwendungsfällen trennen. Anwendungsfälle sind einzelne Abläufe wie „Schaden melden“. Die Hexagonal Architecture legt nur fest, wie Adapter und Anwendungskern zusammenarbeiten, und sagt nichts darüber, wie der Anwendungskern selbst aufgeteilt ist. Dafür teilt die Clean Architecture den Anwendungskern in zwei Teile: die fachlichen Regeln, die für alle Abläufe gelten, und die Anwendungsfälle, die diese Regeln für einen bestimmten Ablauf aufrufen. Gilt die Regel „Schadendatum muss vorhanden sein“ zum Beispiel auch beim Ändern einer Meldung, steht sie einmal bei den fachlichen Regeln, und die Anwendungsfälle „Schaden melden“ und „Schaden ändern“ rufen sie beide auf.
Nachteile und typische Fehler
Für jeden angebundenen Zugang und Dienst braucht das Team einen eigenen Adapter, und auch dieser Code muss gepflegt werden. Bei jedem Übergang zwischen Adapter und Anwendungskern muss der Adapter die Angaben umformen: Der Portaladapter macht aus Formularfeldern die Angaben für „Schaden melden“, der Datenbankadapter macht aus der Meldung Datenbankbefehle. Über die Jahre darf niemand Formularfelder oder Datenbankbefehle in den Anwendungskern schreiben. Am besten prüft ein automatischer Test bei jeder Änderung, ob der Anwendungskern Code aus Webtechnik oder Datenbank verwendet. Die Hexagonal Architecture stellt nicht sicher, dass die Regel im Anwendungskern fachlich richtig ist. Sie nimmt niemandem die Entscheidung ab, welche Angaben Pflicht sind, und sie macht eine langsame Datenbank nicht schneller.
Typische Fehler und woran man sie erkennt:
- Technik im Anwendungskern: Ein Entwickler schreibt Begriffe aus Webtechnik oder Datenbank in die Regeln des Anwendungskerns. Der Anwendungskern liefert bei einer Ablehnung zum Beispiel eine Fehlernummer, die nur Webbrowser auswerten, oder verwendet einen Tabellennamen der Schadendatenbank. Der Makleradapter muss diese Fehlernummer dann erst in eine Antwortnachricht für den Makler übersetzen. Der Anwendungskern hängt dann doch von Webserver oder Datenbank ab. Erkennbar ist das daran, dass ein Wechsel der Datenbank oder des Webservers Änderungen am Anwendungskern verlangt.
- Fachliche Regel im Adapter: Ein Adapter prüft selbst etwas oder ergänzt fehlende Angaben und umgeht damit den Anwendungskern. Der Makleradapter setzt zum Beispiel bei fehlendem Schadendatum das Eingangsdatum ein, damit Maklermeldungen nicht abgewiesen werden. Die Regel des Anwendungskerns gilt dann für Makler nicht mehr. Erkennbar ist das an Maklermeldungen, deren Schadendatum auffällig oft mit dem Eingangsdatum übereinstimmt.
- Kleinteilige Ports: Statt „Schaden melden“ gibt es einzelne Aufrufe wie „Vertragsnummer setzen“ und „Datum setzen“. Jeder Adapter muss die richtige Reihenfolge dieser Aufrufe kennen, und wer den Code liest, erkennt nicht mehr, was die Anwendung anbietet. Erkennbar ist das an Adaptern, die viele Aufrufe in fester Reihenfolge hintereinander absetzen.
- Verschluckter Speicherfehler: Das Speichern in der Schadendatenbank scheitert, aber der Datenbankadapter meldet dem Anwendungskern trotzdem „gespeichert“ zurück. Der Anwendungskern ruft deshalb „Eingang bestätigen“ auf, und die Kundin erhält eine Bestätigung für eine Meldung, die nicht gespeichert ist. Erkennbar ist das an Bestätigungs-E-Mails, zu denen in der Datenbank keine Meldung existiert.
Weiterführend
Verwandte Ansätze
- Onion Architecture: eine verwandte Bauweise, die fachliche Regeln ebenfalls von der technischen Umsetzung trennt.
- Adapter: das Entwurfsmuster für die Übersetzung zwischen zwei Schnittstellen, die nicht zueinander passen.
- Dependency Injection: eine Möglichkeit, die konkreten Adapter beim Programmstart einzusetzen.
- Domain Driven Design: hilft, die fachlichen Begriffe und Regeln des Anwendungskerns zu gestalten.
Werkzeuge und Umsetzungen
Die Hexagonal Architecture ist eine Bauweise, kein Produkt. Diese Werkzeuge können die Trennung zwischen Anwendungskern und Adaptern prüfen oder im Code sichtbar machen:
- ArchUnit: prüft in Java-Projekten, ob der Anwendungskern unerwünscht von technischem Code abhängt.
- jMolecules: kennzeichnet im Java-Code, welcher Teil Port, Adapter oder Anwendungskern ist, und unterstützt passende Prüfungen.
- Spring Modulith: prüft Modulgrenzen in Spring-Anwendungen. Ports und Adapter muss das Team weiterhin selbst passend entwerfen.
Quellen
- Alistair Cockburn: Hexagonal Architecture: Originalaufsatz von 2005, der Ports und Adapter einführt, die Anwendung gleichermaßen von Benutzern, anderen Programmen und automatischen Tests aufrufen lässt und erklärt, dass die Zahl sechs keine Bedeutung hat.
- AWS Prescriptive Guidance: Hexagonal architecture pattern: beschreibt Ports und Adapter, die Testbarkeit ohne Datenbank, typische Einsatzfälle und den Pflegeaufwand zusätzlicher Adapter.