KI Learning Friday Frühstück #2 – Jetzt anmelden!

Titelbild des Gepardec-Beitrags ‚Hexagonale Architektur
  • Software-Modernisierung

Jenseits des Schichtenmodells: Warum Hexagonale Architektur der Schlüssel für langlebige Enterprise-Systeme ist

DAS WICHTIGSTE IN KÜRZE

  • Klassische Schichtenarchitektur verschmilzt Fachlichkeit mit Technik – das System wird schwer testbar und teuer wartbar.
  • Hexagonale Architektur (Ports & Adapters) dreht die Abhängigkeiten um: die Geschäftslogik steht im Zentrum, Technik bleibt austauschbarer Rand.
  • In Kombination mit Domain-Driven Design entsteht ein Rich Domain Model, das UI-, datenbank- und framework-unabhängig bleibt.
  • Der Mehraufwand lohnt sich bei komplexer, langlebiger Fachlogik – nicht bei simplen CRUD-Services.

Warum Enterprise-Systeme mit der Zeit unwartbar werden

Es ist ein Kontrast, der Entwicklerteams und Projektleiter gleichermaßen frustriert: Zu Beginn eines Enterprise-Projekts verläuft die Entwicklung oft rasant. Neue Features werden im Takt der Sprints ausgeliefert und Teams arbeiten hochproduktiv. Mit zunehmender Lebensdauer und wachsendem Feature-Umfang verliert das System jedoch oft seine Agilität. Die Codebasis mutiert schleichend zu einem schwer wartbaren Koloss und das Hinzufügen neuer Funktionen gleicht immer mehr einer Operation am offenen Herzen. Woran liegt das?

Die Antwort findet sich häufig tief im Fundament: in der klassischen Schichtenarchitektur. Was auf dem Papier als saubere Trennung von Präsentation, Logik und Datenhaltung entworfen wurde, weicht unter dem Druck des Projektalltags auf. Die wertvolle Kern-Geschäftslogik verschmilzt zunehmend mit Datenbank-Technologien und den eingesetzten Frameworks. Dadurch wird das System schwer testbar, was dringend notwendige Refactorings blockiert. In der Folge häufen sich technische Schulden an, die Produktivität sinkt und die Kosten für jede weitere Änderung steigen.

Cost-of-Change-Diagramm: Änderungskosten steigen bei unwartbarer Software exponentiell, bei wartbarer Architektur bleiben sie flach

Cost of Change: Ohne saubere Architektur steigen die Änderungskosten mit der Zeit exponentiell.

Einen Ausweg aus dieser Abwärtsspirale bietet die hexagonale Architektur. Dieser Ansatz stellt die langfristige Wartbarkeit in den Fokus, indem er die Fachlichkeit strikt von der Technik trennt. In diesem Artikel zeigen wir, wie du deine Geschäftslogik wieder in den Mittelpunkt rücken und von Framework-Abhängigkeiten befreien kannst. Um den Wert dieser Architektur zu verstehen, werfen wir zunächst aber einen genaueren Blick auf die eigentlichen Ursachen des Problems.

Warum die klassische Schichtenarchitektur zur Falle wird

Klassische Schichtenarchitektur mit Präsentations-, Logik- und Datenhaltungsschicht

Das klassische Modell wirkt sauber – verführt in der Praxis aber zu datenbankgetriebenem Design.

Sie verführt zu datenbank-getriebenem Design

Da die Datenhaltungsschicht das Fundament bildet, beginnt die Entwicklung meist direkt beim Datenmodell. Wir entwerfen Tabellen und JPA-Entitäten, anstatt das eigentliche Geschäftsverhalten in den Fokus zu rücken. Die Folge: Geschäftslogik und Persistenzlogik verschmelzen. Plötzlich muss sich unsere Kernlogik mit Infrastruktur-Details wie Eager vs. Lazy Loading oder dem Management von Datenbank-Transaktionen beschäftigen. Die Technik durchdringt die Fachlichkeit, macht sie schwerer lesbar und diktiert letztendlich die Geschäftslogik. Durch diese Verschmelzung wird das Softwaresystem weniger flexibel und langfristig unwartbar.

Sie versteckt die eigentlichen Use Cases

Eine klassische Schichtenarchitektur stellt keine Regeln für die “Breite” eines Domainservices. Dies kann zu angewachsenen Service-Klassen führen, durch die viele Abhängigkeiten zu den umgebenden Schichten entstehen. Ein eigentlich einfacher und gut testbarer RegisterUserUsecase kann sich so gemeinsam mit vielen anderen User spezifischen UseCases  in einem schwer testbaren UserService verstecken. Die Architektur kommuniziert nicht mehr klar, was das System fachlich leistet. Damit wird das Prinzip der Screaming Architecture verfehlt, wonach der Code seinen fachlichen Sinn anstatt seiner technischen Details offenbaren sollte. Für das Entwicklungsteam wird es dadurch unnötig erschwert, die echten Geschäftsprozesse im Code nachzuvollziehen.

Sie ist anfällig für „Shortcuts“

Unter Zeitdruck fällt es leicht, Ebenen zu überspringen – etwa wenn ein Web-Controller direkt auf ein Datenbank-Repository zugreift. Gemäß der „Broken Windows“-Theorie sinkt mit dem ersten geduldeten Architekturverstoß die Hemmschwelle für weitere. Geschäftslogik wandert schleichend in die Präsentationsschicht, die „Single Source of Truth“ geht verloren.

Sie wächst zu einem schwer testbaren System heran

Wenn Geschäftslogik und Infrastruktur miteinander verschmelzen, lassen sich wichtige Fachregeln nicht mehr isoliert prüfen. Um eine einfache Business-Regel zu testen, muss plötzlich ein Application-Context oder eine Datenbank hochgefahren werden. Dadurch besteht die Gefahr, dass anstelle schneller, fokussierter Unit-Tests langsamere und fehleranfälligere Integrationstests geschrieben werden müssen, was wiederum Feedback-Zyklen für Entwickler:innen von ursprünglich wenigen Sekunden auf Stunden oder Tage verlängern kann.

Sie erschwert paralleles Arbeiten im Team

Da in einer Schichtenarchitektur alles auf der Datenbankschicht aufgebaut ist, kann auch die gleichzeitige Entwicklung ausgebremst werden. Um Teams wirklich zu entkoppeln, müssen Schnittstellen aktiv und diszipliniert gegen die  natürliche Architekturrichtung eingeführt werden. Fehlt diese Disziplin im Projektalltag, können entkoppelnde Interfaces schnell wegfallen. In Kombination mit aufgeblähten Service-Klassen können sich Entwickler:innen dann selbst bei eigentlich unabhängigen Features blockieren. Die Folge sind Merge-Konflikte und unnötige Wartezeiten.

Um diese Abwärtsspirale zu durchbrechen, reicht es nicht, einfach „besser aufzupassen“. Wir brauchen eine Architektur, die uns strukturell dazu zwingt, das Richtige zu tun: Die Hexagonale Architektur.

 

Genau solche gewachsenen Systeme mit technischen Schulden modernisieren wir – wie das sicher und standardkonform abläuft, zeigt unser Secure Java Code Upgrade.

Entkopplung durch Dependency Inversion

In der klassischen Schichtenarchitektur wird das Single-Responsibility-Principle (SRP) oft unbewusst verletzt. Dieses Prinzip besagt, dass eine Klasse nur einen einzigen Grund haben sollte, sich zu ändern. Durch die schleichende Vermischung von Geschäftsregeln und technologischen Entscheidungen geht diese Trennung  jedoch im Projektalltag oft verloren.

Um diese Vermischung aufzulösen und so das SRP architektonisch zu schützen, wird in der hexagonalen Architektur das Dependency-Inversion-Principle (DIP) angewendet. Die  Geschäftslogik darf niemals direkt von Datenbanken, UIs oder Frameworks abhängen. Stattdessen wird die Quellcode-Abhängigkeit umgekehrt: Sowohl Kernlogik als auch technologische Details hängen ausschließlich von abstrakten Schnittstellen ab.

Die Hexagonale Architektur (von ihrem Erfinder Alistair Cockburn ursprünglich „Ports and Adapters“ genannt)  setzt SRP und DIP auf Architekturebene um. Das Modell lässt sich dabei in drei wesentliche Bereiche unterteilen:

Der Anwendungskern (Application):

Enthält die Geschäftslogik und ist dadurch das Herzstück der Software. Der Kern ist reines Java und frei von technischen Framework-Abhängigkeiten der Infrastruktur wie beispielsweise JAX-RS- oder JPA-Annotationen.

Die Ports:

Ports sind die Schnittstellen, über die der Kern mit der Außenwelt kommuniziert. Wir unterscheiden zwischen Driving Ports (treibende Ports, z.B. Use Cases, die von außen aufgerufen werden) und Driven  Ports (getriebene Ports, z.B. ein Interface zum Speichern von Daten, das der Kern aufruft). Wichtig ist, dass die Definitionshoheit und Verantwortung für diese Ports ausschließlich beim Anwendungskern liegt. Konsequent angewandt bedeutet das: Ports sprechen die Sprache der Domäne.  Dadurch wird die Fachlichkeit von technischen Details entkoppelt und diktiert stattdessen Verträge, an die sich die Infrastruktur anpassen muss.

Die Adapter (Infrastruktur):

Externe Systeme verbinden sich niemals direkt mit der Geschäftslogik. Die Kommunikation erfolgt über Adapter. Diese sind technologiespezifisch und  implementieren Ports oder rufen diese  auf. Ein REST-Controller ist ein Primary Adapter, der HTTP-Requests in Methodenaufrufe eines Ports übersetzt. Ein Repository, das via SQL auf eine Datenbank zugreift, ist ein Secondary Adapter, der das Interface eines getriebenen Ports implementiert.

Hexagonale Architektur nach dem Ports-and-Adapters-Muster mit Application Core sowie REST-, JPA- und SOAP-Adaptern

Die goldene Regel: Quellcode-Abhängigkeiten fließen immer nur von außen nach innen.

Die goldene Regel lautet: Quellcode-Abhängigkeiten fließen immer nur von außen nach innen. Die Geschäftslogik kennt keine Adapter, sondern nur die eigenen Ports.

Was die Hexagonale Architektur besser macht

Durch diese strikte Umkehr der Abhängigkeiten lösen wir die zuvor beschriebenen Probleme der Schichtenarchitektur auf:

  • Fachlichkeit statt Datenbank-Fokus: Die Datenhaltungsschicht  ist nicht länger das Fundament der Applikation. Die Geschäftslogik bildet das Zentrum. Wir modellieren zuerst, was das System tun soll, und entscheiden später, wie die Daten persistiert werden. Die Technik ordnet sich der Fachlichkeit unter.
  • Screaming Architecture & fokussierte Use Cases: Anstatt riesige Service-Klassen zu bauen, definieren wir dedizierte Inbound Ports für jeden echten Anwendungsfall (z. B. RegisterUserUseCase, ChangeUserPasswordUseCase). Das System „schreit“ heraus, was es fachlich leisten kann.
  • Verbesserte Testbarkeit: Schnelle Unit-Tests lassen sich zwar auch in einem diszipliniert geführten Schichtenmodell durch konsequentes Mocking erreichen, die Hexagonale Architektur macht die isolierte Testbarkeit allerdings zum architektonischen Standard. Da die Geschäftslogik ohnehin ausschließlich gegen Ports arbeitet, können diese in Tests einfach durch In-Memory-Stubs oder Mocks ersetzt werden. Dadurch lässt sich die Menge an teuren und langsamen Integrationstests reduzieren, was die Testpyramide zugunsten schneller Unit-Tests  verschiebt und Feedback-Zyklen verkürzt.
  • Entblockte Teams: Sobald die Kerndomäne ihre Bedürfnisse über Ports diktiert, werden Workstreams entkoppelt. Ein Teammitglied kann den REST-Adapter entwickeln, das zweite die Geschäftslogik gegen einen simplen In-Memory-Adapter implementieren, und das dritte parallel die komplexe Datenbankanbindung aufbauen. Niemand muss mehr auf die Fertigstellung der Datenhaltungsschicht warten.

Zusammengefasst reduziert die hexagonale Architektur die Gründe, warum sich unsere Kern-Geschäftslogik ändern muss, auf ein Minimum. Wir kapseln die fachliche Komplexität so ab, dass das System UI-unabhängig, datenbankunabhängig und im äußersten Fall sogar framework-unabhängig wird. Ein entscheidender Wettbewerbsvorteil, wenn in einigen Jahren das nächste große Technologie-Update ansteht.

 

Könnte dich auch interessieren: Wie wir ein gewachsenes System in der Praxis modernisiert haben → Case Study eLGK API-Upgrade.

Schichtenarchitektur vs. Hexagonale Architektur im Vergleich

KriteriumKlassische SchichtenarchitekturHexagonale Architektur
Abhängigkeitenbauen auf der Datenbank auf (von oben nach unten)zeigen von außen nach innen zum Fachkern
Im Zentrum stehtdas Datenmodell / die Technikdie Geschäftslogik (Domäne)
Testbarkeitoft nur mit Datenbank/Context – langsamisolierte Unit-Tests über Ports – schnell
Technologiewechselaufwändig, Technik durchdringt die LogikAdapter austauschbar, der Kern bleibt stabil
Am besten geeignet füreinfache CRUD-Anwendungen & Prototypengeschäftskritische, langlebige Enterprise-Systeme

Das Innere des Hexagons: Wenn Hexagonale Architektur auf Domain-Driven Design trifft

Mit der konsequenten Entkopplung durch Ports und Adapter haben wir das Fundament für ein test- und wartbares System geschaffen und unseren Anwendungskern vor technischen Einflüssen geschützt. Doch hier stoßen wir auf eine interessante architektonische Leerstelle: Alistair Cockburns ursprüngliche Definition der Hexagonalen Architektur macht keinerlei Vorgaben darüber, wie das Innere der Applikation strukturiert sein soll. Sie definiert lediglich die Grenzen nach außen.

Füllen wir diesen nun geschützten Raum jedoch weiterhin mit rein prozeduraler Ablauflogik, entsteht ein sogenanntes Anemic Domain Model. In diesem Muster dienen die Domänenobjekte im Kern lediglich als reine Datenhalter, während die eigentliche Geschäftslogik in ausgelagerte Service-Klassen wandert. Bei komplexen Systemen führt diese Trennung von Daten und Verhalten erneut zu einer erschwerten Wart- und Testbarkeit.

Der logische nächste Schritt auf dem Weg zu einer reifen Enterprise-Architektur ist daher, dieses Muster zu überwinden und Daten mit Verhalten zu vereinen. Genau hier liefert Domain-Driven Design (DDD) das nötige Paradigma. Die Notwendigkeit, das Innere der Applikation besser zu strukturieren, spiegelt auch die Evolution der modernen Softwarearchitektur wider: Spätere Ansätze setzten exakt an Cockburns Leerstelle an. Sowohl Jeffrey Palermos Onion Architecture als auch Robert C. Martins Clean Architecture platzierten das Domain-Driven Design explizit in ihrem Zentrum.

In unserer heutigen Projektpraxis verschmelzen diese Konzepte daher zu einem pragmatischen Best-of: Wir nutzen die strikte Entkopplungs-Mechanik der Hexagonalen Architektur nach außen und füllen den Kern im Inneren mit einer starken, verhaltensorientierten DDD-Struktur.

Die Anatomie des Kerns: Application- und Domain Hexagon

Um diese starke DDD-Struktur umzusetzen, unterteilen wir den isolierten Anwendungskern in zwei ineinandergreifende Schichten: das Application- und das Domain Hexagon.

Anwendungskern aus Application Hexagon (Use Cases) und Domain Hexagon (Aggregates) mit Ports und Adaptern

Der Kern in zwei Schichten: das Application Hexagon (Use Cases) orchestriert, das Domain Hexagon (Aggregates) entscheidet fachlich.

  • Das Application Hexagon (Der Orchestrator):Diese äußere Schicht des Kerns fungiert als Vermittler zwischen der technologischen Außenwelt (den Adaptern) und den eigentlichen Geschäftsregeln. Hier leben unsere Application Services, welche Anwendungsfälle der Fachlogik kapseln (auch Use Cases (z. B. RegisterUserUseCase)) sowie die Definitionen der Ports. Das Wichtigste: Das Application Hexagon ist dünn, enthält also keine tiefen Business-Regeln. Es orchestriert lediglich den Workflow: Es nimmt Anfragen entgegen, holt Daten über Secondary Ports, delegiert die fachliche Arbeit an das Domain-Modell (ohne dabei selbst fachliche Entscheidungen zu treffen) und persistiert die Ergebnisse.
  • Das Domain Hexagon (Das Herzstück): Im absoluten Zentrum finden wir die Elemente, deren Verantwortlichkeit es ist, die Kernprobleme unserer Software zu lösen. Um die Geschäftslogik strukturiert abzubilden, greift das Hexagon auf die Bausteine des Domain-Driven Designs (DDD) zurück:
    • Entities: Fachliche Objekte mit einer eindeutigen Identität, welche über ihren gesamten Lebenszyklus hinweg bestehen bleibt, auch wenn sich ihre Eigenschaften ändern (z. B. ein User oder eine Order).
    • Value Objects: Unveränderliche Objekte ohne eine eigene Identität, die sich nur durch den Wert ihrer Eigenschaften definieren (z. B. ein Geldbetrag oder eine Adresse).
    • Aggregates: Logische Cluster aus Entities und Value Objects, die als eine transaktionale Einheit behandelt werden. Der Einstiegspunkt – die sogenannte Aggregate Root – stellt sicher, dass alle fachlichen Regeln (Invarianten) innerhalb dieses Clusters stets gültig bleiben.
    • Domain Events: Fachlich relevante Ereignisse, die festhalten, dass etwas Wichtiges in der Domäne passiert ist (z. B. ein UserRegisteredEvent), worauf andere Teile des Systems reagieren können.

Diese Aufteilung ist kein architektonischer Selbstzweck, sondern der Schlüssel, um ein großes Problem gewachsener Enterprise-Systeme zu lösen: die Entstehung unübersichtlicher „Fat Services“. Durch die Trennung greift das SRP auf höchster Ebene, da sich Workflows im Application Hexagon anpassen lassen, ohne kritische Geschäftsregeln im Domain Hexagon abzuändern. Gleichzeitig ermöglicht die völlige Isolierung der Fachlogik von externen Abhängigkeiten eine hohe Testbarkeit durch schnelle Unit-Tests ohne komplexe Mocks. Zuletzt sorgt die klare Rollenverteilung (die Orchestrierung (das Wann) lebt im Use Case, die fachliche Validierung (das Wie) im Domänen-Objekt) durch eine erhöhte Lesbarkeit für eine kognitive Entlastung im Entwicklungsteam. 

Rich Domain Model: Warum Hexagonale Architektur und DDD das perfekte Paar sind

Daten und Verhalten werden im Domain Hexagon zu einem Rich Domain Model vereint. Die Domain-Klassen kapseln nicht nur den Zustand, sondern auch die exakten Geschäftsregeln, wie sich dieser Zustand ändern darf.

Während DDD auch in einer klassischen Schichtenarchitektur umsetzbar ist, kann die Abhängigkeit des Domain-Modells vom Datenbank-Modell zu einer Verschmelzung der beiden führen. Das entstehende Problem: Frameworks wie Hibernate zwingen uns technische Kompromisse auf. Sie benötigen leere Default-Konstruktoren und verhindern die Nutzung von unveränderlichen Feldern. Dadurch wird die fachliche Kapselung systematisch untergraben. Entwickler:innen könnten so theoretisch ein Domänen-Objekt in einem ungültigen Zustand erzeugen.

Und genau an diesem Punkt zeigt sich, warum die Hexagonale Architektur der ultimative Enabler für echtes DDD ist: Sie schützt die Domäne vor Infrastruktur-Vorgaben. Da die Hexagonale Architektur das klassische JPA-Entity in den äußeren Datenbank-Adapter verbannt, werden die Domänen-Objekte im Kern echte, reine Java-Klassen. Diese lassen sich nun  mit aussagekräftigen Konstruktoren ausstatten, die ungültige Zustände verbieten. Setter können komplett entfernt und durch rein fachliche Methoden ersetzt werden. Die Geschäftslogik hat die absolute Kontrolle über sich selbst.

Diese neu gewonnene Freiheit ermöglicht es, eine der wichtigsten Regeln von Domain-Driven Design konsequent in Code zu gießen: die Ubiquitous Language. Da im Kern keine Rücksicht mehr auf Datenbanktabellen, Framework-Konventionen oder HTTP-Verben genommen werden muss, können Klassen, Methoden und Ports exakt so benannt werden, wie die Fachexperten sprechen. Der Code liest sich nicht länger wie ein technisches Handbuch, sondern wird zur lebendigen, ausführbaren Dokumentation des eigentlichen Geschäftsprozesses.

Komplexe Fachregeln prüfen: Drei Lösungswege für die Praxis

Der Anwendungskern ist wie in den vorhergehenden Kapiteln erläutert konzeptionell in zwei Ebenen unterteilt: das Application- und das Domain Hexagon. Um die Domain im Zentrum maximal testbar und isoliert zu halten, operiert sie im Idealfall befreit von jeglichem Systemwissen und definiert keine eigenen Ports.

In der Praxis stehen Entwicklungsteams an dieser Stelle jedoch regelmäßig vor einer architektonischen Hürde: Wie prüft eine Domain-Klasse eine Invariante, wenn sie zwingend externe Daten benötigt, aber selbst keine Ports aufrufen soll?

Beispiel aus einer echten Fachdomäne: Ein Kunde beantragt einen neuen Kredit. Die Unternehmensvorgabe lautet: „Die Gesamtverschuldung eines Kunden aus allen laufenden Krediten darf 50.000 Euro nicht übersteigen.“ Das Problem: Das Kreditantrag-Aggregat kennt nur seinen eigenen Betrag, nicht die Summe aller anderen Kredite in der Datenbank.

Um dieses Problem zu lösen, greifen wir in unseren Projekten primär auf drei etablierte Lösungswege zurück, die jeweils unterschiedliche Trade-offs mit sich bringen.

 

Data Pre-Fetching (Die Orchestrierung im Use Case)

Hierbei orchestriert der Application Service (Use Case) die benötigten Daten über einen Outbound-Port, bevor er die eigentliche Domäne aufruft. Er fragt die Datenbank nach der Summe der bisherigen Kredite des Kunden und übergibt diesen reinen Datenwert als Parameter an die Entität (z. B. kreditantrag.pruefeLimit(bisherigeVerschuldung)). Ist die Fachlogik zur Auswertung der Daten zu komplex für das reine Aggregat, lässt sich dieser Weg auch mit einem Domain Service kombinieren. Der Use Case reicht die gefetchten Daten dann als Parameter an einen KreditPruefungsService (ohne eigene Ports) weiter.

Betrachten wir diesen Ansatz durch die Brille der Clean Architecture von Robert C. Martin, ist er das Idealbild der Aufgabentrennung:

  • Die Enterprise Business Rule: Die Regel „Maximal 50.000 Euro Verschuldung“ gehört in das Zentrum der Domäne. Die Entität kapselt das Wissen, wo die Grenze liegt und wie sie geprüft wird.
  • Die Application Business Rule: Die Notwendigkeit, eine verteilte Datenbank abzufragen, um Akten zu summieren, ist ein rein systemischer Workflow. Der Use Case steuert den Ablauf, sammelt den Kontext und reicht ihn an das Domänenmodell weiter.

Der Trade-off (Die DDD-Perspektive): Aus Sicht der perfekten Kapselung entsteht an dieser Stelle ein Riss, der oft als Leaking Domain Logic kritisiert wird. Wenn der Use Case wissen muss, dass die Summe der Altkredite benötigt wird, um den Antrag zu prüfen, liegt ein Teil der Geschäftsregel (das „Was wird benötigt?“) im Application Layer. Das Domänenmodell gibt damit einen Teil seiner Logik preis.

Domain Services mit Ports (Information Hiding)

Erfordert die Komplexität der Fachdomäne eine konsequentere Auslegung des Domain-Driven Designs, bei der die Domäne die absolute strukturelle Kontrolle über alle Geschäftsabläufe behält, kann das benötigte Interface direkt im Domain-Layer definiert werden. Ein Domain Service ruft dieses Interface auf, holt die Summe und führt die Validierung durch. Die tatsächliche Implementierung des Interfaces liegt weiterhin ganz außen in der Adapter-Schicht.

Betrachten wir diesen Ansatz durch die Brille des Domänenmodells, ist er das Idealbild der Kapselung:

  • Information Hiding: Das Domänenmodell verhält sich wie eine echte Blackbox. Es entscheidet völlig autark, welche Daten es für eine komplexe Geschäftsregel benötigt, und besorgt diese über selbst definierte Interfaces.
  • Kein Leaking Logic: Der Use Case muss die Geschäftsregeln nicht kennen. Wenn sich die Regel ändert und für die Kreditprüfung plötzlich auch noch ein Kredit-Score geladen werden muss, ändert sich nur die Domäne. Der Use Case bleibt davon völlig unberührt.

Der Trade-off (Die Clean-Architecture-Perspektive):
Aus der Perspektive der Clean Architecture vermischt dieser Weg die Zuständigkeiten, da Infrastruktur-Bedenken in den innersten Kern der Anwendung eindringen. Gemäß dieser Architekturlehre ist die Orchestrierung externer Datenzugriffe eine Application Business Rule, die im Application Layer verortet sein sollte, während das Domänenmodell als reines, von Außensystemen unabhängiges Regelwerk agiert. 

Für das Entwicklungsteam entsteht in der Praxis zudem eine erhöhte Komplexität durch die Zersplitterung der Ports. Da der Application Layer weiterhin Ports für Aufgaben wie die abschließende Persistenz (z. B. einen RepositoryPort) benötigt, verteilen sich die Outbound-Ports nun über zwei Schichten. Dies zwingt das Team zu der kontinuierlichen Abwägung, ob die Schnittstelle eine fachliche Bedingung für den Domain Layer darstellt oder lediglich einen technischen Workflow bedient. Nicht zuletzt erschwert dieser Ansatz die Testbarkeit, da in den Unit-Tests des Domänen-Kerns nun zwingend mit Mocks gearbeitet werden muss.

Hexagonale Architektur mit Application Service, Domain Service und Aggregates zur Prüfung komplexer Fachregeln

Lösungsweg 2: Der Domain Service prüft die Invariante über ein selbst definiertes Interface – die Domäne behält die Kontrolle.

Deferred Validation durch Domain Events

In stark asynchronen oder hoch skalierenden Systemen kann die Prüfung auf Event-Ebene verlagert werden. Die Domain-Klasse nimmt die Zustandsänderung vor und veröffentlicht ein Domain Event (z. B. KreditBeantragtEvent). Ein Event-Handler im Application Layer fängt dieses Ereignis ab, führt die externe Validierung durch und entscheidet, ob die Transaktion committed oder durch eine Kompensationslogik (Saga Pattern) zurückgerollt wird. Die fachliche Invariante wird hier nicht blockierend, sondern auf Basis von Eventual Consistency geschützt.

Fazit – Bewusste Trade-offs

Welchen der Wege man wählt, ist letztlich keine Frage von richtig oder falsch, sondern eine Entscheidung zwischen verschiedenen Architekturphilosophien.

Der “Data Pre-Fetching” -Ansatz orientiert sich stark an der Clean Architecture. Er betrachtet die Orchestrierung von externen Datenzugriffen als die Kernaufgabe des Application Layers. Der Vorteil liegt in der simplen Testbarkeit der Domäne, da sie völlig frei von technischen Schnittstellen und Mocks bleibt. Der Trade-off: Das Domänenmodell gibt einen Teil seiner strikten Kapselung auf, da der Use Case wissen muss, welche Daten für die Regelprüfung vorab geladen werden müssen.

Der Ansatz über „Domain Services mit Ports“ vertritt die Philosophie eines konsequenten Domain-Driven Designs. Hier wird Information Hiding maximiert: Das Domain-Hexagon agiert als autarke Blackbox und definiert seine Schnittstellen zur Außenwelt selbst. Der Trade-off dieser maximalen Kapselung ist eine höhere strukturelle Komplexität im Domain-Layer und die zwingende Notwendigkeit von Mocks, um die Domäne in Unit-Tests zu validieren.

Die Wahl der Strategie ist kontextabhängig. Für viele Enterprise-Projekte erweist sich das Pre-Fetching aufgrund seiner unkomplizierten Testbarkeit als effizienter Sweet-Spot. Besitzt das Projekt jedoch ein hochkomplexes, sich häufig wandelndes Regelwerk, bei dem das „Was muss geladen werden?“ von tiefen fachlichen Bedingungen abhängt, spielen Ports in Domain Services ihre volle Stärke aus. Events wiederum sind das Mittel der Wahl, sobald der Gesamtprozess ohnehin auf Asynchronität und eventualer Konsistenz aufbaut.

Hexagonale Architektur in der Praxis: Probleme, Lösungen und Pragmatismus

Die Theorie hinter der Hexagonalen Architektur ist bestechend logisch. Der Projektalltag verlangt Entwicklungsteams jedoch oft einiges an Übersetzungsleistung ab. In der Praxis prallen Theorie und Realität regelmäßig aufeinander. Wie gelingt also der Spagat zwischen sauberer Entkopplung und produktivem Code, ohne dass das System an architektonischem Perfektionismus erstickt?

Mapping und CQRS

Die Antwort auf diese Frage findet sich meist direkt an den Ein- und Ausgängen des Hexagons. Hier setzen wir auf zwei Konzepte, die sich in der Praxis perfekt ergänzen: das Architekturmuster CQRS (Command Query Responsibility Segregation) und den strategischen Einsatz von Mapping-Mustern.

CQRS lehrt uns eine essenzielle Grundregel für robuste Enterprise-Systeme: Zustandsverändernde Schreiboperationen (Commands) und reine Leseabfragen (Queries) haben fundamental unterschiedliche Anforderungen an Komplexität, Konsistenz und Performance. Ein System liest Daten anders, als es sie schreibt. Behalten wir diese Trennung von Lesen und Schreiben im Hinterkopf, wenn wir uns nun einem klassischen Konfliktpunkt widmen: Wie schützen wir unsere Domäne beim Schreiben von Daten vor den Vorgaben der Infrastruktur?

Um das Domänenmodell von Infrastruktur-Vorgaben zu befreien, ist es notwendig an den Architekturgrenzen mappen. In der Softwarearchitektur gibt es hierfür nicht den einen richtigen Weg, sondern verschiedene Strategien, die wir je nach Kontext stets gegeneinander abwägen müssen:

  • No-Mapping: Web-, Domänen- und Datenbankmodell nutzen exakt dieselbe Klasse (z.B. die JPA-Entity).
    • Vorteil: Kein Boilerplate-Code, schnelle initiale Entwicklung und die Möglichkeit, ORM-Features (wie Lazy Loading) direkt auf Modellebene zu nutzen.
    • Nachteil: Infrastruktur-Details infizieren schleichend die Geschäftslogik (SRP-Verstoß).
  • Full-Mapping: Jeder Use Case definiert für jeden Ein- und Ausgang völlig eigene Modelle, die separat gemappt werden.
    • Vorteil: Absolute, feingranulare Entkopplung.
    • Nachteil: Hoher Wartungsaufwand und potenziell unübersichtliche „Mapping-Hölle“.
  • Two-Way-Mapping: Die Domäne besitzt ein reines Kern-Modell. Die Web- und Persistenzschicht definieren ihre eigenen Modelle und mappen jeweils von außen auf diesen Kern.

In vielen unserer Enterprise-Projekte hat sich das Two-Way-Mapping als Standard etabliert.  Der initiale Mapping-Aufwand wird bewusst in Kauf genommen, da diese Schicht als essenzieller Anti-Corruption Layer fungiert. 

Gerade bei der Modernisierung gewachsener Systeme treffen wir häufig auf historische, überladene Datenbanktabellen. Ein sauberes Domain-Driven Design erfordert hingegen strikte, fokussierte Transaktionsgrenzen in Form von kleinen Aggregates. Ein naives 1:1-Mapping der Datenbankstruktur wäre hier fatal, da es diesen historischen Ballast direkt in die Kerndomäne schleppen würde. Stattdessen nutzen wir das Mapping gezielt als Werkzeug, um diesen Konflikt durch asymmetrisches Design aufzulösen: Eine einzige, komplexe JPA-Entität kann – je nach Use Case – in unterschiedliche, schlanke Domänen-Objekte übersetzt werden. 

So kann die Persistenzschicht ihre mächtigen ORM-Features weiterhin voll ausreizen, während die Geschäftslogik aus reinen, typsicheren POJOs besteht. Zudem machen wir die Architektur evolutionär: Wenn zukünftige Use Cases neue Spalten erfordern oder Datenbankstrukturen aus Performancegründen umgebaut werden müssen, fängt der Mapping-Layer diese Erschütterungen ab. Bestehende Domänen-Objekte müssen nicht angepasst werden. Der unweigerlich entstehende Mapping-Aufwand lässt sich durch Tools wie Mapstruct abfedern. Der Gewinn: Die Kernlogik bleibt dauerhaft von Datenbank-Spezifika befreit und blitzschnell testbar.

Natürlich darf an dieser Stelle nicht verschwiegen werden, dass jede Form des Mappings ihren Preis hat: erhöhte Systemkomplexität. Zusätzliche Mapping-Schichten bedeuten mehr Klassen, teilweise redundante Datenstrukturen und trotz Toolunterstützung einen stetigen Wartungsaufwand bei Modelländerungen. Mapping ist keine kostenlose Abstraktion.

Um nicht in die Falle des Overengineerings zu tappen, muss diese Architekturentscheidung stets durch echten Mehrwert legitimiert sein. Wir bezahlen den Preis der Mapping-Komplexität ganz bewusst dort, wo er sich rentiert – nämlich beim Schutz unserer Kerndomäne vor historischem Ballast, bei der Anbindung an Fremdsysteme oder der Stabilisierung von APIs. Wo dieser Mehrwert fehlt, darf die Architektur pragmatisch bleiben.

Für einfache, reine Lese-Operationen (z. B. das Laden einer tabellarischen Übersicht für ein UI) ist der Weg über das komplexe Domänenmodell samt Mapping beispielsweise oft Overkill. Hier bietet sich ein pragmatischer Einsatz des  CQRS-Patterns (Command Query Responsibility Segregation) an. Während schreibende, komplexe Use Cases zwingend durch den strikt hexagonalen Domänenkern gehen müssen, um Geschäftsregeln zu berücksichtigen und Invarianten zu schützen, verändert reines Lesen den Zustand nicht. Daher stellt es keinen Architekturverstoß dar, wenn dedizierte Query-Usecases im Application Layer die Domäne umgehen und flache DTOs für das UI direkt aus Datenbanken und Fremdsystemen projizieren. Die Grenzen des Domain-Hexagons gelten weiterhin dort, wo Geschäftslogik wirklich ausgeführt wird.

 

Übrigens lässt sich ein Teil dieser Modernisierung automatisieren – mehr dazu in unserem Beitrag Transformer-Ansatz für automatisierte Java-Modernisierung.

Die Framework-Frage: Annotationen und Exceptions

Eine der häufigsten Praxisfragen lautet: „Wenn der Kern frei von Technik sein soll, wie gehen wir dann mit Framework-Features wie Datenbank-Transaktionen, HTTP-Fehlern oder Dependency Injection um?“ Die Antwort liegt in einer klaren Arbeitsteilung und gesundem Pragmatismus:

Der pragmatische Umgang mit Annotationen (@Inject & @Transactional): Nach der reinen architektonischen Lehre müssten Transaktionssteuerung und Lifecycle-Management über Factory-Klassen strikt vom Anwendungskern getrennt werden. Im Ökosystem moderner Frameworks wie Spring oder Quarkus entscheiden wir uns jedoch oft bewusst für die Produktivität: Wir lassen leichtgewichtige Infrastruktur-Annotationen direkt an den Use Cases im Application-Layer zu. Wir setzen hierbei auf standardisierte Jakarta EE-Schnittstellen anstelle von proprietären Framework-Annotationen. Das minimiert den „Vendor Lock-in“ und hält den Code portabel. Ein @Transactional ist im Application Layer architektonisch sogar sinnvoll platziert, da der Use Case ohnehin den atomaren Workflow orchestriert. Das @Inject wiederum bewahrt das Team vor großen Mengen Boilerplate-Code. Da diese Annotationen rein deklarativ sind, stören sie isolierte Unit-Tests nicht – sie werden bei direkter Instanziierung schlicht ignoriert. Die geschäftskritische Domänenlogik im Zentrum bleibt davon absolut unangetastet.

Wie werden Fehler kommuniziert?
Der isolierte Kern wirft ausschließlich fachliche Exceptions (z. B. UserAlreadyExistsException). Er kennt kein HTTP. Es ist die alleinige Aufgabe des Inbound-Adapters (z. B. via ExceptionMapper), diese fachliche Ausnahme abzufangen und in die technologische Sprache der Außenwelt zu übersetzen.

Die eiserne Regel lautet dabei: Solange die harten Geschäftsregeln im Domain Layer unangetastet reines Java bleiben und Infrastruktur-Spezifika strikt in den Adaptern verbleiben, ist die Kern-Fachlichkeit gesund.

 

Bevor du weiterliest: Genau diese standardkonforme Modernisierung – von JBoss EAP bis Jakarta EE – bieten wir als eigene Leistung an → JBoss EAP 8 Upgrade.

 

Architekturgrenzen absichern

Sind die Architekturregeln und pragmatischen Ausnahmen einmal definiert, dürfen sie im Projektalltag nicht schleichend aufgeweicht werden. Da der bloße Appell an die Entwickler:innen-Disziplin unter Zeitdruck oft nicht ausreicht, sollten die Grenzen technisch abgesichert werden. Hierfür haben sich in der Praxis zwei Wege bewährt:

  • Physische Trennung durch Modulschnitt:Das Projekt wird in separate Build-Module unterteilt. Das domain-Modul ist völlig autark und hat keinerlei externe Abhängigkeiten. Das application-Modul kennt lediglich die Domäne und – passend zum pragmatischen Ansatz – ausschließlich explizit freigegebene, standardisierte APIs. Schwere Framework-Implementierungen oder Datenbank-Technologien dürfen nur vom adapter-Modul konsumiert werden. Der große Pluspunkt: Dieser Weg erzwingt die architektonische Disziplin mit absoluter Sicherheit direkt auf Compiler-Ebene.
  • Logische Trennung durch Architektur-Tests:Als leichtgewichtigere Alternative lassen sich Tools wie ArchUnit in der Test-Suite verankern. Initial einmalig sauber konfiguriert, schlagen diese Tests beim Build sofort fehl, sobald sich unerlaubte Abhängigkeiten einschleichen. So wird das System verlässlich vor dem „Broken Windows“-Effekt bewahrt, ohne den Overhead von Multi-Module-Setups in Kauf nehmen zu müssen.

Weder physische Modulschnitte noch Testing-Frameworks wie ArchUnit sind exklusive Werkzeuge der Hexagonalen Architektur. Beide Ansätze eignen sich ebenso, um die Grenzen einer klassischen Schichtenarchitektur zu schützen (etwa um einen direkten Zugriff von der Web- auf die Datenbankschicht zu unterbinden). Der entscheidende Unterschied liegt nicht in den eingesetzten Werkzeugen, sondern in den Regeln, die wir damit erzwingen: Bei der Hexagonalen Architektur zeigen alle Abhängigkeiten konsequent nach innen zur Kern-Fachlichkeit.

 

Wie sich solche Architekturregeln automatisch im Build absichern lassen, zeigt unsere CI/CD-Automatisierung.

Kosten-Nutzen-Analyse: Wann lohnt sich zusätzlicher Aufwand?

Die Einführung der Hexagonalen Architektur erfordert zweifellos einen höheren initialen Aufwand. Zum einen verlangt der Wechsel vom gewohnten datenbankzentrierten Denken hin zur Domänenzentrierung eine gewisse Lernkurve.  Zum anderen führt die strikte Trennung von fachlichen Domain-Objekten und technischen Persistence-Entities unweigerlich zu mehr Boilerplate-Code. Das weckt in Entwicklungsteams oft die berechtigte Sorge vor einem ausufernden Mapping-Overhead. Die logische Frage lautet dann: Wozu dieser architektonische Aufwand für einen simplen Datenbankzugriff? Und ab wann rechnet sich dieses Investment?

Die ehrliche Antwort lautet – wie so oft in der Softwareentwicklung – „It depends“. Wenn wir einen simplen CRUD-Microservice entwickeln, einen schnellen Prototyp bauen oder die Anwendung generell keine komplexe Geschäftslogik enthält, kann ein Anemic Domain Model in einer klassischen Schichtenarchitektur völlig ausreichen. Daten primär von der UI in die Datenbank zu schieben, erfordert kein Hexagon.

Der wahre Glanz der Hexagonalen Architektur entfaltet sich bei hoher Domain-Komplexität: Handelt es sich um eine geschäftskritische Enterprise-Anwendung? Ändern sich die Business-Regeln regelmäßig? Arbeiten mehrere Entwickler:innen über Jahre hinweg an derselben Code-Basis? Genau hier kippt die Kosten-Nutzen-Rechnung zugunsten des Hexagons und die Angst vor Overhead relativiert sich schnell. Der anfängliche Schmerz beim Einrichten der Mapper und Ports ist dann lediglich ein einmaliges Investment. Das Mapping ist keine Schikane, sondern eine schützende Firewall des Systems. Sobald diese Grenzen stehen, amortisiert sich der initiale Mehraufwand durch eine erhöhte Test- und Wartbarkeit und die daraus resultierenden geringeren Änderungskosten.

Wie lassen sich die unweigerlichen Lernkurven bei der Einführung meistern? Die Antwort liegt in einem bewussten architektonischen Pragmatismus. Anstatt sich in dogmatischen Diskussionen zu verlieren, hat es sich in der Praxis bewährt, standardisierte Framework-Features dort zu nutzen, wo sie Entwicklerteams produktiv machen, und harte architektonische Grenzen exakt dort zu ziehen, wo sie die Fachlichkeit schützen.

Letztlich ist saubere Softwarearchitektur niemals ein akademischer Selbstzweck, sondern eine wirtschaftliche Investition in die Zukunft eines Produkts. Die Kombination aus Hexagonaler Architektur und Domain-Driven Design ist ein mächtiges Werkzeug, um dem schleichenden Anwachsen von Technical Debt systematisch entgegenzuwirken. 

Indem wir technologische Abhängigkeiten konsequent an den Rand drängen und die reine Geschäftslogik in das Zentrum stellen, schaffen wir Enterprise-Systeme, die flexibel bleiben. Das ultimative Ziel dieser Architektur ist erreicht, wenn das Hinzufügen eines komplexen Features im fünften Jahr der Entwicklung noch genauso schnell, sicher und fehlerfrei funktioniert wie am allerersten Tag.

Dein Java-Enterprise-System soll langlebig bleiben?

Wir modernisieren gewachsene Java-Anwendungen strukturell – von Jakarta EE bis Quarkus, testbar und zukunftssicher.

+

Häufige Fragen

Literaturverzeichnis

Cockburn, A., & Garrido de Paz, J. M. (2024). Hexagonal architecture explained. Humans and Technology Press.

Fowler, M. (2003). Anemic domain model. martinfowler.com.

Hombergs, T. (2023). Get your hands dirty on clean architecture (2nd ed.). Packt Publishing.

Khononov, V. (2022). Einführung in Domain-Driven Design. dpunkt.verlag.

Martin, R. C. (2018). Clean Architecture. mitp.

Vieira, D. (2023). Designing hexagonal architecture with Java. Packt Publishing.

geschrieben von:
Florian F., Erhard, Sascha
WordPress Cookie Plugin von Real Cookie Banner