Hexagonale Architektur (auch Ports and Adapters, geprägt von Alistair Cockburn) wird oft mit Schichtenarchitektur verwechselt oder zu einem Ordnerschema verkürzt. Dabei steckt hinter dem Konzept eine einzige, ziemlich scharfe Regel:

Abhängigkeiten zeigen immer nach innen, zur Fachlichkeit. Die Fachlichkeit kennt weder Datenbank noch Framework noch HTTP.

Das Bild dazu

flowchart LR
    subgraph Aussen["Adapter (außen)"]
        REST["REST-Controller"]
        JPA["JPA-Repository"]
        MAIL["Mail-Client"]
    end

    subgraph Innen["Anwendungskern"]
        IN["Eingehender Port
BestellungAufgeben"] UC["Use Case
und Domäne"] OUT["Ausgehender Port
BestellungenSpeichern"] end REST --> IN IN --> UC UC --> OUT JPA -. implementiert .-> OUT MAIL -. implementiert .-> OUT

Die gestrichelten Linien sind der entscheidende Punkt: Der Adapter implementiert ein Interface, das der Kern besitzt. Der Kern ruft nie eine Datenbank-Klasse direkt auf. Das nennt man Dependency Inversion.

Ein kleines Beispiel

Der ausgehende Port ist ein Interface im Kern:

// Gehört zum Anwendungskern, kein JPA, kein Spring
public interface BestellungenSpeichern {
    void speichern(Bestellung bestellung);
}

Der Use Case nutzt nur dieses Interface:

public class BestellungAufgeben {

    private final BestellungenSpeichern speicher;

    public BestellungAufgeben(BestellungenSpeichern speicher) {
        this.speicher = speicher;
    }

    public void ausfuehren(Bestellung bestellung) {
        bestellung.pruefen();          // Fachregeln liegen in der Domäne
        speicher.speichern(bestellung);
    }
}

Erst der Adapter am Rand kennt die Technik:

@Repository
class BestellungenJpaAdapter implements BestellungenSpeichern {

    private final BestellungJpaRepository jpa;

    BestellungenJpaAdapter(BestellungJpaRepository jpa) {
        this.jpa = jpa;
    }

    @Override
    public void speichern(Bestellung bestellung) {
        jpa.save(BestellungEntity.from(bestellung));
    }
}
Merke
Nicht die Ordnerstruktur macht die Architektur hexagonal, sondern die Richtung der Abhängigkeiten. Prüfen lässt sich das automatisiert, zum Beispiel mit ArchUnit.

Was du davon hast

  • Fachlogik lässt sich ohne Datenbank und ohne Framework testen.
  • Technische Entscheidungen (Datenbank, Messaging, Framework) bleiben austauschbar.
  • Der Kern wird zur Dokumentation dessen, was das System fachlich tut.

Was das kostet und wann es sich nicht lohnt, schreibe ich im Artikel Wann Hexagonal Overengineering ist.