[0:00] Bevor wir in die hexagonale  Architektur einsteigen,   [0:03] möchte ich mit euch drei Fragen klären. Erstens,  was ist überhaupt Softwarechitektur? Zweitens,   [0:11] was sind die Ziele einer Software Architektur?  Und drittens, wie schneidet die allseits   [0:16] beliebte Schichtenarchitektur im Hinblick  auf diese Ziele ab? Beginnen wir mit der   [0:22] ersten Frage. Was ist Software Architektur?  Nehmen wir mal an, wir wollen eine webbasierte   [0:28] Dienstleistung zur Verfügung stellen.  Dann haben wir zuerst einmal die User,   [0:33] die benutzen einen Webbrowser und der greift über  HTTP auf die Software zu. Und in der Software   [0:40] haben wir verschiedene Komponenten, die zum einen  untereinander kommunizieren und zum anderen mit   [0:47] externen Komponenten wie einer Datenbank oder  der Schnittstelle zu einem Drittanbietersystem.   [0:53] Wir könnten auch eine App zur Verfügung stellen.  Auch die kommuniziert mit dieser Software. Dafür   [0:59] brauchen wir vielleicht eine weitere Komponente,  und die App in sich hat natürlich auch wieder eine   [1:04] Architektur. Die Architektur, das ist also  die Aufteilung der Software in Komponenten,   [1:11] die Anordnung und die Eigenschaften  dieser Komponenten und die Art und Weise,   [1:16] wie diese Komponenten miteinander interagieren.  Was ist das Ziel einer Software Architektur? Eine   [1:24] gute Antwort auf diese Frage hat Robert Martin  in seinem Buch Clean Architecture gegeben: "The   [1:29] goal of a software architecture is to minimize the  human resources required to build and maintain the   [1:36] required system." Und weiter: "If that effort is  low and stays low throughout the lifetime of the   [1:42] system, the design is good." Wenn wir anfangen,  das auf Deutsch zu übersetzen, dann klingt das so:   [1:50] "Das Ziel einer Software Architektur ist es, den  Personalaufwand für die Erstellung und Wartung des   [1:55] gewünschten Systems zu minimieren." Das hört sich  an, als hätte es ein Jurist geschrieben. Deshalb   [2:01] hier meine Definition: "Eine gute Architektur  erlaubt es Software über ihre gesamte Lebensdauer   [2:08] mit gleichbleibend geringem Aufwand zu ändern."  Aufwand könnt ihr in der Regel gleichsetzen mit   [2:14] Kosten. Eure Arbeit- oder Auftraggeber:innen  möchten Software mit möglichst geringen und   [2:20] langfristig planbaren Kosten ändern können. Und  Änderungen, das sind zum einen die Umsetzung von   [2:27] Userwünschen, aber auch wenn die User gerade  gar keine Wünsche haben, sind oft Änderungen   [2:32] nötig. Z. B. wenn Sicherheitslücken in einer  Drittanbieterkomponente entdeckt werden und die   [2:37] Komponente aktualisiert oder ausgetauscht werden  muss. Oder wenn Drittanbieter ihre Schnittstellen   [2:43] ändern. Ich war mal in einem Projekt, da haben  wir Login über Facebook angeboten, und ich glaube,   [2:48] ich habe alle 6 Monate eine E-Mail von Facebook  mit Ankündigungen von Schnittstellenänderungen   [2:53] bekommen. Oder auch wenn sich Gesetze ändern.  Viele von euch mussten sicherlich schon mal   [2:59] Software an die DSGVO anpassen. Das Ziel muss  es also sein, um es noch mal mit den Worten von   [3:05] Bob Martin auszudrücken: "To keep Software soft."  Und das erreichen wir, indem wir die Anwendung in   [3:12] Komponenten aufteilen, die möglichst gekoppelt  sind und unabhängig voneinander entwickelt und   [3:17] getestet werden können. Ein gutes Beispiel ist das  Internet. Es besteht aus Millionen von Servern,   [3:23] die relativlose gekoppelt sind und die sich  im Laufe der Jahre und Jahrzehnte unabhängig   [3:29] voneinander mit gleichbleibend überschaubarem  Aufwand modernisieren oder ersetzen lassen.   [3:36] Wie versuchen wir das in der Regel bei Software  umzusetzen? Mit der Schichtenarchitektur. Die   [3:42] kennen wir alle. Wir haben hier in der  Regel die Präsentationsschicht mit der   [3:46] Benutzerschnittstelle, die Anwendungsschicht mit  der Geschäftslogik und die Datenhaltungsschicht   [3:52] mit z. B. einer relationalen Datenbank und  einem OR-Mapper wie Hibernate. In vielen   [3:58] Projekten bleibt es allerdings nicht lange bei  dieser sauberen Trennung. Oft beginnt es damit,   [4:03] dass wir unter Zeitdruck ein Feature umsetzen  müssen, und dazu greifen wir ausnahmsweise von   [4:09] der Präsentationsschicht unter Umgehung der  Geschäftslogik auf die Datenhaltung zu - oder   [4:14] auch einfach direkt auf Hibernate - oder aus der  Geschäftslogik heraus auf Hibernate. Das ist sogar   [4:20] erlaubt. Das nennt sich dann "offene" oder "nicht  strenge" Schichtenarchitektur. Und dann wollen wir   [4:26] unsere Anwendung irgendwann erweitern. Vielleicht  wollen wir ein externes System anbieten. Dafür   [4:32] setzen wir eine Client Library in die Logikschicht  und kommunizieren ausschließlich über die   [4:38] Logikschicht mit dem externen System. Aber dann  müssen wir doch wieder schnell etwas umsetzen und   [4:43] greifen - natürlich nur vorübergehend - aus der  Präsentationsschicht direkt auf diese Library zu.   [4:50] Und eine App wollen wir auch noch unterstützen.  Dazu bauen wir ganz sauber eine REST-API, die nur   [4:56] auf die Logikschicht zugreift. Aber dann sind da  ja noch diese nützlichen Validierungsfunktionen   [5:02] in der Präsentationsschicht, die wir auch in der  REST-API gut gebrauchen können. Und dann kommt   [5:08] da wieder dieses Feature, das schnellstmöglichst  fertig werden muss. Und wir umgehen ausnahmsweise   [5:13] noch einmal die Logikschicht. Und wenn wir  jetzt noch direkt auf Hibernate zugreifen   [5:18] oder den Drittanbieter-Client, dann fällt das  auch niemandem mehr auf. Und plötzlich ist von   [5:23] unserer anfangs so sauberen Architektur nicht mehr  viel zu sehen. Die Schichtengrenzen sind verwischt   [5:29] und alle bauen Code dort an, wo es ihnen gerade  passt. Das führt zu einer extrem hohen Kopplung.   [5:36] Die Komponenten lassen sich nicht mehr isoliert  voneinander testen und wir brauchen plötzlich   [5:40] Tools wie Powermock. Powermock zu benutzen, das  ist so, als würdet ihr Risse im Fundament eures   [5:47] Hauses mit Farbe überstreichen. Ihr versteckt  grundlegende Mängel in der Architektur,   [5:53] anstatt sie zu beheben. Und irgendwann stürzt das  Haus ein. Wenn wir hier Hibernate auf die nächste   [5:59] Major-Version upgraden wollen und es Änderungen an  der API gibt, dann müssen wir Code in all diesen   [6:05] Schichten anpassen. In einem meiner letzten  Projekte wurde eine uralte EclipseLink-Version   [6:11] verwendet und die konnten wir aus genau diesem  Grund nicht aktualisieren. Wir hätten alle   [6:16] Schichten anpassen müssen, und das hätte Wochen  gedauert. Und deshalb hat das Management das   [6:21] Update immer wieder herunterpriorisiert. Und  so konnten wir nie neue JPA-Features nutzen,   [6:28] und wir konnten auch viele andere Libraries nicht  aktualisieren, weil deren Dependencies sich mit   [6:33] denen der alten EclipseLink-Version überschnitten  haben. Wenn die Software erstmal so aussieht,   [6:39] dann ist sie viel viel anfälliger für Bugs, und  der Aufwand für Fixes und Änderungen schießt in   [6:46] die Höhe - und damit auch die Kosten. Das muss  natürlich nicht so sein. Ich sage hier nicht,   [6:53] dass die Schichtenarchitektur grundsätzlich  schlecht ist. Ich habe viele Projekte gesehen,   [6:57] die erfolgreich damit umgesetzt wurden. Aber  die Gefahr ist eben sehr hoch, dass sich eine   [7:02] anfangs saubere Schichtenarchitektur zu einer  unsauberen, verworren Architektur entwickelt,   [7:08] wie eben dieser hier. Alister Cockburn hat 2005  in einem Blogartikel eine Softwarechitektur   [7:15] vorgestellt, die genau diese Probleme  eliminieren soll: die hexagonale Architektur.   [7:23] Aus diesem Blogartikel lassen sich drei Ziele  herauslesen. Erstens, die Anwendung - hier   [7:30] als Hexagon dargestellt - soll gleichermaßen von  Nutzerinnen und Nutzern, anderen Anwendungen oder   [7:37] automatisierten Tests gesteuert werden können.  Für die Geschäftslogik, die im Kern der Anwendung   [7:43] sitzt, soll es keinen Unterschied machen, ob  sie beispielsweise von einem User Interface,   [7:48] einer REST-API oder einem Test-Famework  aufgerufen wird. Der Kern der Anwendung   [7:54] soll also von steuernden Komponenten, von  steuernder Infrastruktur isoliert sein. Zweitens,   [8:02] die Geschäftslogik im Anwendungskern soll isoliert  von der Datenbank und sonstiger Infrastruktur wie   [8:09] z. B. Mailversand und Drittanbieter-API entwickelt  und getestet werden können. Aus Sicht der   [8:15] Geschäftslogik soll es keinen Unterschied machen,  ob Daten z. B. in einer relationalen Datenbank,   [8:22] einer NoSQL-Datenbank oder serialisiert im File  System gespeichert werden. Der Anwendungskern soll   [8:29] also auch von gesteuerter Infrastruktur isoliert  sein. Und drittens, Infrastrukturkomponenten   [8:37] sollen einfach austauschbar sein. Also z. B. eine  Aktualisierung der Datenbank oder der ORM-Library,   [8:43] die Anpassung an geänderte externe Schnittstellen  oder eine Neuimplementierung des User Interfaces.   [8:50] Eine Modernisierung der Infrastruktur soll also  ohne Änderungen an der Geschäftslogik möglich   [8:55] sein. Das ist ein bisschen so wie Autofahren.  Wir können Straßen erneuern, Straßenführungen   [9:02] ändern oder Verkehrsschilder austauschen, ohne  dass ihr deswegen ein neues Auto braucht. Das sind   [9:08] die Ziele. Wie kann die hexagonale Architektur  diese Ziele erreichen? Wie funktioniert sie?   [9:15] Dieses Hexagon stellt den Kern der Anwendung dar.  Der Kern enthält in der Regel Modellklassen und   [9:21] die Geschäftslogik der Anwendung. Hier gibt es  keinerlei technische Details wie REST-Controller   [9:26] oder JPA-Repositories. Wir haben hier rein  fachlichen Code. Und hier ist die Infrastruktur.   [9:34] Die Komponenten der Infrastruktur habe  ich erstmal als Fragezeichen dargestellt,   [9:37] denn wenn wir die Geschäftslogik entwickeln,  dann wissen wir eventuell noch gar nicht,   [9:42] welche Infrastrukturkomponenten wir benötigen. Wie  kann der Kern nun mit der Außenwelt kommunizieren?   [9:49] Der Kern definiert Schnittstellen, die  sogenannten Ports und an diese Ports   [9:55] schließen wir die sogenannten Adapter an, und die  wiederum kommunizieren mit den Komponenten der   [10:00] Infrastruktur. Und jetzt kommt der entscheidende  Punkt: Die Geschäftslogik kennt ausschließlich die   [10:07] Ports. Die Adapter und die Infrastruktur dahinter  sind für sie nicht sichtbar. Das könnt ihr   [10:14] vergleichen mit einem Mischpult. Es hat Eingänge,  an die ihr z. B. ein Mikrofon, ein Keyboard oder   [10:21] eine E-Gitarre anschließen könnt, und es hat  Ausgänge, an die ihr Lautsprecher, Kopfhörer   [10:28] oder ein Aufnahmegerät anschließen könnt. Aber  wie diese Geräte intern funktionieren, das spielt   [10:34] für das Mischpult keine Rolle. Und genau aus  dieser Analogie stammt die Bezeichnung "Port".   [10:42] An diese Ports könnt ihr jedes Gerät anschließen,  das den mechanischen und elektrischen Protokollen   [10:47] des Ports entspricht. Das war jetzt erstmal  ziemlich abstrakt, deshalb zeige ich euch als   [10:53] nächstes ein ganz konkretes Beispiel. Wir beginnen  wieder mit dem Kern der Anwendung. Die Anwendung   [11:00] soll eine Benutzerregistrierung entgegennehmen.  Dafür definieren wir einen Registrierungsport.   [11:06] Wie die Anwendung die Registrierung entgegennimmt,  ob über eine Webseite, eine Desktopanwendung   [11:11] oder eine REST-API ist für die Entwicklung der  Geschäftslogik erstmal irrelevant. Dann soll die   [11:18] Anwendung die Benutzerdaten irgendwo speichern.  Dafür definieren wir einen Persistenzport. Wo   [11:24] und wie die Daten gespeichert werden, ist  erstmal nicht wichtig. Und dann wollen wir   [11:29] noch eine Willkommensnachricht verschicken. Dafür  definieren wir einen Versandport. Und auch wie   [11:34] genau wir die Nachrichten verschicken, ob über  einen lokalen oder öffentlichen Mailserver oder   [11:39] einen Unified-Messaging-Anbieter, auch das  spielt aus Sicht der Anwendung keine Rolle.   [11:45] An diese Ports können wir jetzt Adapter  anschließen. Für die Registrierung können   [11:51] wir eine Webseite implementieren, über die sich  Benutzerinnen und Benutzer über ihren Webbrowser   [11:56] anmelden können. Persistieren können wir  die Daten in einer SQL-Datenbank. Dafür   [12:02] könnten wir einen JPA-Adapter z. B. mit Hibernate  implementieren. Und zum Versand der Nachrichten   [12:09] benutzen wir unseren firmeninternen Mailserver,  den wir mit einem SMTP-Adapter anbinden. Für die   [12:16] Registrierung wollen wir neben der Webseite auch  noch eine REST-API anbieten. Dafür können wir an   [12:22] unseren bestehenden Eingabeport einfach noch einen  zweiten Adapter anschließen, einen REST-Adapter,   [12:28] auf den dann externe Anwendungen zugreifen  können. Was genau passiert nun in diesen Ports   [12:35] und Adaptern bei der Registrierung? Dazu werde  ich einmal diesen linken Teil heranzoomen. Die   [12:44] Nutzerin oder der Nutzer füllt im Browser ein  Formular aus und klickt auf "Absenden". Der   [12:50] Browser schickt daraufhin die Formulardaten  als POST-Request an den Web-Adapter. Der   [12:56] Adapter wertet den POST-Request aus und ruft eine  `registerPort.registerUser()` Methode auf. Genauso   [13:03] könnte eine externe Anwendung die Nutzerdaten  als JSON-Paket an den REST-Adapter schicken.   [13:10] Der wertet das JSON-Paket aus und ruft wieder die  gleiche `registerUser()`-Methode auf, die auch der   [13:16] Web-Adapter aufgerufen hat. Schauen wir einmal auf  die andere Seite - auf diesen rechten Teil. Um die   [13:24] Nutzerin oder den Nutzer zu speichern, ruft die  Geschäftslogik eine `saveUser()`-Methode auf dem   [13:29] Persistance Port auf. Der JPA-Adapter generiert  für diesen Aufruf ein SQL-Kommando und schickt das   [13:37] an die Datenbank. Wie genau der JPA-Adapter das  SQL generiert - ob er dafür einen StringBuilder   [13:43] einsetzt oder Hibernate oder EclipseLink - und  welche Version dieser Libraries er verwendet - das   [13:49] spielt aus Sicht des Anwendungskerns keine Rolle.  Der Kern ist vollständig vom Adapter isoliert. Wie   [13:57] diese Isolierung funktioniert, zeige ich euch  gleich. Ich zoome wieder heraus. Und wie euch   [14:02] sicher aufgefallen ist, gibt es zwei Arten  von Ports und Adaptern: Auf der linken Seite   [14:08] haben wir solche, die die Anwendung steuern,  und auf der rechten Seite haben wir solche,   [14:13] die von der Anwendung gesteuert werden. Die  Steuernden nennen wir primäre oder treibende Ports   [14:20] und Adapter und die gesteuerten bezeichnen wir  als sekundäre oder getriebene Ports und Adapter.   [14:28] Durch die Ports und Adapter werden Anwendungskern  und Infrastruktur an dieser Grenze voneinander   [14:33] isoliert. Technische Details wie JPA, Hibernate,  RESToder JSON sind nur in den Adaptern sichtbar,   [14:41] nicht im Anwendungskern. Der Kern weiß nicht,  welche Technologien und Libraries wir außen   [14:47] herum verwenden, und es spielt für ihn auch  keine Rolle. Das ist so wie bei dem Mischpult   [14:52] vorhin und z. B. der E-Gitarre. Für das Mischpult  spielt es keine Rolle, aus welchem Material die   [14:58] Gitarre gebaut ist, welche Saiten aufgezogen  sind oder wer die Gitarre spielt. Jetzt habe   [15:05] ich heute schon sehr oft das Wort "Isolierung"  genannt. Isolierung, das hört sich erstmal gut an,   [15:11] und das lässt sich auf Bildern auch schön  darstellen. Doch wie funktioniert das in   [15:16] der Praxis? Wie lässt sich Isolierung in  der Software, im Quellcode realisieren?   [15:23] Das möchte ich euch an einem Klassendiagramm  zeigen. Das hier ist unser Anwendungshexagon.   [15:30] Darin haben wir die Implementierung der  Userregistrierung im `RegisterUserService`.   [15:35] Die öffentliche Schnittstelle dieses Services  definieren wir außerdem in einem Interface im   [15:40] `RegisterUserPort`, und der Service implementiert  dieses Interface. Auf dieses Interface greift dann   [15:48] ein REST-Controller zu, und der benutzt dann z.  B. RESTEasy. Wenn wir den Code jetzt auf zwei   [15:54] Module aufteilen, z. B. zwei Maven-Module, dann  haben wir hier ein Application- Modul und hier   [16:01] ein Controller-Modul. Das Controller-Modul hat  Abhängigkeiten auf das Application-Modul und auf   [16:07] RESTEasy. Und da die Dependency von Controller  zu Application geht und nicht anders herum,   [16:13] hat das Application-Modul auch keine Dependency  auf RESTEasy. Der Code im Application-Modul weiß   [16:21] also nichts über die Technologie, die  im Controller-Modul eingesetzt wird.   [16:27] Auf der anderen Seite haben wir unseren  JPA-Adapter mit einer Dependency auf   [16:31] Hibernate. Wie kann jetzt der Anwendungscode im  `RegisterUserService` den JPA-Adapter aufrufen?   [16:39] Wenn wir hier eine Dependency einrichten, dann  hat das Application-Modul auch eine transitive   [16:45] Dependency auf Hibernate - und damit nicht  nur das Application-Modul, sondern auch das   [16:50] Controller-Modul. Und genau diesen Rattenschwanz  an Dependencies wollen wir bei der hexagonalen   [16:56] Architektur vermeiden. Wir wollen zwar von hier  innen Code da draußen aufrufen, aber wir wollen   [17:04] keine Dependency in diese Richtung und deshalb  drehen wir die Dependency um. Wir invertieren   [17:11] sie, und zwar mit dem "Dependency Inversion  Principle", dem D aus SOLID. Und das geht so:   [17:19] Wir setzen in das Application Hexagon  noch ein Interface, den `PersistancePort`.   [17:25] Der Service hat eine Abhängigkeit auf das  Interface, und der JPA-Adapter implementiert   [17:32] das Interface und wird per Dependency Injection  mit dem Service verbunden. Und damit geht   [17:38] zwar der Aufruf von innen nach außen, aber die  Quellcodeabhängigkeit von außen nach innen. Und   [17:46] damit hat der Anwendungskern keine Abhängigkeit  auf den JPA-Adapter und auch keine indirekte   [17:52] Abhängigkeit auf Hibernate. Der Kern hat also kein  Wissen über irgendwelche technischen Details. Er   [17:58] kennt weder RESTEasy noch JPA oder Hibernate. All  diese technischen Details liegen ausschließlich   [18:06] außerhalb des Kerns in den Adaptern. Alle  Abhängigkeiten zeigen von außen nach innen   [18:13] zum Anwendungskern. Und das ist ein grundlegendes  Prinzip der hexagonalen Architektur, und das hat   [18:19] auch einen Namen: Dependency Rule. Die Dependency  Rule besagt, dass alle Quellcodeabhängigkeiten von   [18:27] außen Richtung Anwendungskern gerichtet sein  müssen. Und damit ist die Dependency Rule wie   [18:34] ein Schutzschild für den Anwendungskern:  Sie schirmt den Kern vor allen technischen   [18:39] Einflüssen von außen ab. Hier seht ihr noch mal  die Gesamtarchitektur. Alle Abhängigkeiten führen   [18:47] zum Kern. Und das ermöglicht die Isolierung, die  die hexagonale Architektur so mächtig macht. Aber   [18:54] es macht auch mehr Arbeit. Und damit kommen wir  zum Thema "Mapping". Wenn wir mit JPA arbeiten,   [19:02] dann sieht unsere User-Klasse in der Regel  irgendwie so aus. Die hat annotierte Felder   [19:08] und entsprechende Abhängigkeiten zu JPA. Wo  setzen wir diese Klasse hin? Schauen wir noch   [19:14] einmal auf das Klassendiagramm, und jetzt zoomen  wir in diesen rechten Teil hinein. Wenn wir unsere   [19:22] User-Klasse hier in den Anwendungskern setzen,  dann brauchen wir für die Annotationen eine   [19:28] Abhängigkeit vom Kern auf Hibernate. Die haben  wir aber gerade erst mühsam durch die Dependency   [19:34] Inversion entfernt und wir haben gelernt, dass  die Dependency Rule, eines der grundlegenden   [19:41] Prinzipien der hexagonalen Architektur, genau das  nicht erlaubt. Wenn wir die User-Klasse hier raus   [19:48] in das Adapter-Modul setzen, dann liegt zwar die  Abhängigkeit auf Hibernate innerhalb des Adapters,   [19:55] aber der `RegisterUserService` muss die  User-Klasse ja auch kennen, und damit   [19:59] haben wir wieder die Dependency Rule verletzt. Um  dieses Dilemma aufzulösen, gibt es verschiedene   [20:05] Mapping-Strategien. Ich werde an dieser Stelle  nur eine dieser Strategien vorstellen, die meiner   [20:10] Erfahrung nach in der Regel sinnvollste, und  zwar das Zwei-Wege-Mapping - oder auf englisch:   [20:16] Two-Way Mapping. Am Ende der Präsentation findet  ihr einen Link zu einem Artikel, in dem ich auch   [20:22] einige andere Mapping-Strategien beschreibe. Beim  Two-Way Mapping gibt es zwei User-Klassen: eine   [20:29] im Anwendungskern und eine im Adapter. Die heißt  jetzt `JpaUser`, um nicht mit der User-Klasse im   [20:36] Kern verwechselt zu werden. Die `JpaUser`-Klasse  sieht so aus, also so wie die User-Klasse,   [20:42] die ich vorhin gezeigt habe. Die hat annotierte  Felder, Dependencies auf JPA und in der Regel   [20:48] noch Getter und Setter. Die User-Klasse im Kern  hingegen sieht so aus: Die hat ähnliche Felder,   [20:56] aber keine JPA-Annotationen. Dafür z. B. einen  Konstruktor, der die Parameter validiert und   [21:02] dann ein neues gültiges User-Objekt erzeugt mit  den erforderlichen Feldern, wie hier z. B. auch   [21:08] dem Registrierungsdatum. Also alles rein fachliche  Angelegenheiten. Und wenn wir mit einem reichen   [21:15] Domainmodell arbeiten, dann hat diese Klasse  auch noch Methoden, mit denen wir den Zustand des   [21:20] User-Objekts ändern können. Wir haben also diese  zwei User-Klassen und jetzt ist es die Aufgabe des   [21:27] JPA-Adapters, beim Aufruf der `saveUser()`-Methode  das User-Objekt aus dem Anwendungsmodell auf ein   [21:34] `JpaUser`-Objekt zu mappen und dieses dann über  Hibernate zu persistieren. Der JPA-Adapter hat   [21:41] natürlich auch noch eine `loadUser()`-Methode,  die lädt ein `JpaUser`-Objekt über Hibernate,   [21:48] mappt dieses auf ein Anwendungs-User-Objekt und  gibt das dann an den aufrufenden Code aus dem   [21:52] Kern zurück. Als Code sieht das etwa so aus: Hier  die save-Methode, die mappt das User-Objekt auf   [22:02] ein `JpaUser`-Objekt und persistiert das dann  - hier z. B. über ein Spring Data Repository.   [22:09] Und die load-Methode sieht so aus: Sie lädt das  `JpaUser`-Objekt über das Spring Data Repository   [22:17] und mappt das dann auf einem `User`-Objekt bzw.  ein `Optional` davon. Diesen Mapper, den müsst   [22:24] ihr nicht selbst implementieren. Dafür gibt es  Libraries wie z. B. MapStruct und ModelMapper.   [22:30] Wenn ihr googelt, findet ihr noch ein paar andere  wie JMapper, Dozer und Orika, aber die werden seit   [22:35] einigen Jahren nicht weiterentwickelt. So ein  Mapping ist nicht nur beim JPA-Adapter sinnvoll,   [22:42] sondern auch beim REST-Adapter. Und dafür  zoomen wir einmal in diesen Bereich hinein.   [22:49] Hier ist unsere `User`-Klasse - und oft wollen  wir nicht alle Attribute einer Entity über die   [22:53] REST-API sichtbar machen. Bei der `User`-Klasse  z. B. könnten wir ein internes Feld verstecken   [22:59] wollen oder die ID der Person, die diese Notiz  gemacht hat. Außerdem müssen wir für Datums- und   [23:04] Zeitfelder definieren, wie diese in dem JSON-Paket  formatiert sein sollen. Eine `User`-Klasse für den   [23:11] REST-Controller könnte z. B. so aussehen: Die  benutzt eine JSON-Annotation, um den Timestamp   [23:17] zu formatieren und hat dementsprechend eine  Abhängigkeit beispielsweise auf Jackson.   [23:22] Und auch diese technische Abhängigkeit wollen  wir nicht im Anwendungskern haben. Also - hier   [23:28] wieder das Klassendiagramm - also platzieren  wir das `UserDto` zusammen mit der JSON-Library   [23:36] außerhalb des Kerns in das Controller-Modul.  Und der REST-Controller mappt nun in der   [23:42] `registerUser()`-Methode das `UserDto`-Objekt  auf ein `User`-Objekt. Und der Controller hat   [23:49] natürlich auch eine `getUser()`-Methode,  und in der mappt er das `User`-Objekt aus   [23:54] dem Anwendungskern auf ein `UserDto`. So  ein Mapping ist übrigens nicht nur bei   [24:00] der hexagonalen Architektur sinnvoll - auch die  Schichtenarchitektur kann davon profitieren. Aber   [24:06] bei der hexagonalen Architektur ist es zwingend  notwendig, um die technischen Abhängigkeiten   [24:11] aus dem Kern fernzuhalten und gleichzeitig  die Dependency Rule nicht zu verletzen. Und   [24:18] jetzt kommen wir zu einem der großen Vorteile  der hexagonalen Architektur, der Testbarkeit.   [24:25] Am Anfang habe ich über die Ziele einer  Softwarearchitektur gesprochen, und da   [24:29] habe ich als eine der Anforderungen isoliert  testbare Komponenten genannt. Und in der Tat   [24:35] macht es die hexagonale Architektur sehr einfach,  Komponenten isoliert zu testen. Den Anwendungskern   [24:44] können wir ganz einfach testen, indem wir ihn  über einen primären Port aufrufen und den oder   [24:49] die vom Test betroffenen sekundären Ports mit  Test Doubles verbinden. Das Test Double soll   [24:55] in seiner Rolle als Spy die Aufrufe aus dem Kern  protokollieren und in der Rolle als Stub Antworten   [25:02] des Adapters simulieren. Der Test verifiziert  dann die Antwort des Anwendungskerns und die   [25:09] protokollierte Interaktion mit dem Adapter. Wir  können nicht nur die Geschäftslogik isoliert von   [25:14] den Adaptern testen, sondern auch andersherum die  Adapter isoliert von der Geschäftslogik. Hier ist   [25:22] der REST-Adapter. Testen können wir den z. B. mit  REST Assured. Das schickt einen HTTP-Request an   [25:30] den Adapter, und der REST-Adapter ruft dann ein  Test Double des primären Anwendungsports auf. Das   [25:36] Test Double soll wieder die Aufrufe protokollieren  und eine Antwort des Anwendungskerns simulieren.   [25:43] Und der Adapter schickt dann eine Antwort zurück  an den Test. Dort kann dann wieder die Antwort   [25:49] verifiziert werden und die Interaktion mit dem  Anwendungskern. Hier ist der JPA-Adapter. Hier   [25:57] können wir von unserem Test aus mit Testcontainers  eine Datenbank hochfahren und mit Testdaten   [26:03] füllen. Dann ruft der Test den Adapter auf und  der sendet eine SQL Query an die Datenbank. Die   [26:10] Datenbank schickt ihre Antwort an den JPA-Adapter,  und der schickt eine Antwort an den Test. Im Test   [26:17] können wir wieder die Antwort verifizieren und  die erwarteten Änderungen in der Datenbank.   [26:22] Neben diesen Integrationstest sollten wir  natürlich unabhängig von der Architektur   [26:27] auch immer End-to-End-Tests schreiben.  Wenn wir den Blick noch einmal auf die   [26:31] gesamte Anwendung werfen, dann können all diese  Komponenten isoliert voneinander getestet werden.   [26:38] Genauso wie auch E-Gitarre, Keyboard, Mikrofon,  Mischpult, Kopfhörer, Lautsprecher und Recorder   [26:45] unabhängig voneinander getestet werden können.  Wo wir hier gerade noch einmal das komplette   [26:51] Hexagon sehen - Alistair Cockburn wurde in einem  Interview gefragt: "Warum eigentlich ein Sechseck?   [26:56] Hat das irgendeine besondere Bedeutung?" Und  Cockburns Antwort war überraschend pragmatisch:   [27:02] Er wollte eine Form verwenden, die noch keiner  verwendet hat. Vierecke werden überall verwendet,   [27:07] Fünfecke sind schwer zu zeichnen, also wurde es  ein Sechseck. Eine weitere Frage, die oft gestellt   [27:13] wird, ist: "Was ist der Unterschied zwischen  hexagonaler Architektur und Ports & Adapters?"   [27:19] Die Antwort ist ganz einfach. Es gibt keinen.  Beide Begriffe bezeichnen dieselbe Architektur.   [27:26] Der offizielle Name, den Alister Cockburn seiner  Architektur gegeben hat, ist "Ports & Adapters".   [27:33] Die Bezeichnung "hexagonale Architektur" ergibt  sich aus der grafischen Darstellung, die ihr heute   [27:38] viele Male gesehen habt. Alistair Cockburn hat in  dem Interview, das ich eben erwähnt habe und zu   [27:43] dem ich euch nachher noch einen Link mitgebe,  verraten, dass auch er den bildlichen Namen,   [27:48] also "hexagonale Architektur" vorzieht, aber  dass der offizielle Name einer Architektur   [27:54] einer sein muss, der deren Eigenschaften  beschreibt. Und das tut "Ports & Adapters"   [27:59] eben besser als "hexagonale Architektur". Und  damit haben wir die hexagonale Architektur - oder   [28:05] die Ports-and-Adapters-Architektur von  allen Seiten betrachtet. Wir haben den   [28:11] Anwendungskern mit der Geschäftslogik und ohne  jegliche technischen Details. Wir haben Ports,   [28:18] die im Code einfach Interfaes sind. Wir haben  Adapter, in denen die technischen Details liegen   [28:24] und die wir an diese Ports anschließen. Primäre  Adapter rufen primäre Ports auf. Sekundäre Adapter   [28:31] implementieren sekundäre Ports. Wir haben die  Infrastruktur, mit der die Adapter kommunizieren,   [28:39] und wir haben unseren Schutzschild, die Dependency  Rule, die festlegt, dass alle Abhängigkeiten   [28:44] Richtung Kern zeigen müssen. Und jetzt möchte ich  uns die Ziele einer Softwarearchitektur noch mal   [28:50] in Erinnerung rufen und abgleichen, inwieweit die  hexagonale Architektur diese Ziele erfüllt. Hier   [28:57] sind noch mal die Ziele: Software soll leicht  änderbar sein, und das soll sie während ihrer   [29:01] gesamten Lebensdauer bleiben. Und das erreichen  wir, indem wir sie in lose gekoppelte Komponenten   [29:07] strukturieren, die unabhängig entwickelbar  und testbar sind. Gehen wir die Kriterien mal   [29:13] im Einzelnen durch. Änderbarkeit. Wir können die  Geschäftslogik im Kern unserer Anwendung ändern,   [29:21] ohne dass wir die Adapter oder die Infrastruktur  ändern müssen, solange wir die Ports unverändert   [29:26] lassen. In der Praxis geht natürlich eine Änderung  der Geschäftslogik auch oft mit Änderungen am   [29:32] User Interface und den Daten einher, sodass das  zugegebenermaßen nicht das Killerargument ist.   [29:38] Viel wichtiger ist, dass wir die Adapter und auch  die von ihnen benutzten Libraries wie RESTEasy,   [29:44] Jackson, Hibernate, aber auch die  Komponenten der Infrastruktur wie   [29:48] z. B. die Datenbank aktualisieren oder komplett  austauschen können, ohne dass wir auch nur eine   [29:55] Zeile Code in der Geschäftslogik ändern  müssen. Wenn wir hier z. B. Hibernate   [30:00] auf die nächste Major Version upgraden wollen  und das Upgrade Änderungen im Code erfordert,   [30:06] dann ist davon ausschließlich der JPA-Adapter  betroffen, denn der ist die einzige Komponente,   [30:11] die eine Abhängigkeit auf Hibernate hat. Um noch  mal die Auto-Analogie aufzugreifen: Wenn ihr eure   [30:17] Reifen wechseln wollt, dann braucht ihr vielleicht  andere Felgen, aber ihr braucht keine neue Achse,   [30:22] kein neues Getriebe und keinen neuen Motor. Am  Ende der Präsentation verlinke ich euch eine   [30:28] Artikelserie, in der ich demonstriere, dass selbst  das Application Framework ein austauschbares   [30:33] technisches Detail der Infrastruktur sein kann.  Und zwar ersetze ich dort Spring durch Quarkus,   [30:40] ohne auch nur eine Zeile Code im Anwendungskern  zu ändern. Noch ein großer Vorteil, den ich unter   [30:46] Änderbarkeit einsortiere, ist der folgende:  Und zwar könnt ihr mit der Entwicklung des   [30:51] Anwendungskerns beginnen und definiert erstmal nur  die Interfaces für die Ports und testet gegen die,   [30:57] ohne dass ihr euch Gedanken über die Adapter  machen müsst - oder über die Technologien,   [31:02] die ihr dahinter einsetzen wollt. Also ob  RESToder GraphQL, ob Spring Data oder Panache,   [31:09] ob SQL oder NoSQL. Und so könnt ihr die  Entscheidung über die Technologien in   [31:14] der Infrastruktur so lange hinauszögern, bis  ihr bei der Entwicklung der Geschäftslogik so   [31:21] viel Erfahrung über eure Anwendung gesammelt  habt, dass ihr viel besser entscheiden könnt,   [31:26] welche Technologien am besten geeignet sind.  Also z. B. in welcher Art von Datenbank sich   [31:30] eure Daten am besten speichern lassen. Kommen  wir zum zweiten Punkt, der losen Kopplung.   [31:37] Eine lose Kopplung erreichen wir zum einen ganz  klar durch die Isolierung zwischen den Ports und   [31:42] den Adaptern und zum anderen dadurch, dass wir die  Komponenten auf separate Module aufteilen können,   [31:49] wie hier z. B. Maven Module. Fachliche Themen  werden ausschließlich im Anwendungsmodul   [31:55] implementiert und alle technischen Themen in einem  oder mehreren Adaptermodulen. Die Abhängigkeiten   [32:01] zwischen den Modulen definieren wir entsprechend  der Dependency Rule. Und so stellen wir sicher,   [32:07] dass keine technischen Abhängigkeiten in den Kern  gelangen und dass wir alle Verantwortlichkeiten   [32:12] eindeutig im Code lokalisieren können. Denn jetzt  müssen wir beim Anlegen einer neuen Klasse genau   [32:19] überlegen, in welches Modul die Klasse gehört.  Denn wenn wir sie ins falsche Modul legen,   [32:24] dann erreichen wir entweder von der neuen Klasse  aus nicht den Code, den wir aufrufen wollen,   [32:30] oder wir erreichen die neue Klasse nicht von dem  Code aus der sie aufrufen soll. Und genau dieses   [32:37] "neuen Code mal eben irgendwo dran bauen", das ist  etwas, zu dem die Schichtenarchitektur mit ihren   [32:42] zahllosen transitiven Dependencies leider  regelrecht verführt. Der dritte Punkt ist   [32:49] unabhängige Entwicklung. Wenn der Anwendungskern  mit seinem Port einmal definiert ist, dann kann   [32:55] die weitere Arbeit an den Komponenten sehr gut  z. B. auf mehrere Pairs aufgeteilt werden. Und   [33:01] viertens, die unabhängige Testbarkeit. Das habe  ich euch vor ein paar Minuten erst gezeigt. Wir   [33:06] können durch den Einsatz von Test Doubles alle  Komponenten vollständig isoliert voneinander   [33:12] testen. Die hexagonale Architektur erfüllt damit  alle Kriterien einer guten Softwarearchitektur.   [33:20] Solltet ihr also nur noch mit der hexagonalen  Architektur arbeiten? Nein. Die hexagonale   [33:27] Architektur hat natürlich auch einen Nachteil.  Das braucht ihr nicht zu lesen. Das ist ein   [33:33] Screenshot aus meiner IDE - aus einem Projekt  mit einem Anwendungsmodul, drei Adaptermodulen   [33:39] und außerdem noch einem separaten Modellmodul  und einem Bootstrap-Modul. Die Aufteilung des   [33:46] Codes in Module, das Implementieren der Ports und  der Adapter und des Mappings stellt einen enormen   [33:53] Mehraaufwand dar. Wenn ihr in jedem dieser  Module nachher nur eine Klasse liegen habt,   [33:58] z. B. bei einem einfachen Microservice, der eine  einzige Entity in die Datenbank schreibt und aus   [34:03] ihr ausliest, dann lohnt sich dieser Mehraaufwand  nicht. Aber für eine große Enterprise-Anwendung,   [34:09] wo jedes dieser Module nachher hunderte  von Klassen enthält, da kann sich der   [34:13] Aufwand schnell amortisieren, und ihr könnt  langfristig Aufwand und Kosten sparen. Gut ist,   [34:20] wenn ihr eine Person im Team habt, die schon erste  Erfahrung mit der hexagonalen Architektur gemacht   [34:24] hat und die abschätzen kann, ob sich dieser  Meeraufwand für euer Projekt lohnt. Oder noch   [34:30] besser: Entwickelt einfach mal selbst eine kleine  Anwendung nach hexagonaler Architektur, sammelt   [34:36] damit erste Erfahrung, und dann könnt ihr die  Person sein, die bei eurem nächsten Projekt die   [34:41] hexagonale Architektur ins Spiel bringt. Und damit  sind wir am Ende angekommen. Hinter dem QR-Code   [34:48] findet ihr die versprochenen Links und die Slides,  und wenn ihr an mehr Content von mir interessiert   [34:53] seid (seltener über Architektur, eher über  neue Java Features), dann schaut gerne mal bei   [34:59] HappyCoders.eu vorbei oder bei einem der anderen  Kanäle. Ich danke euch für eure Aufmerksamkeit.