Hexagonal Architecture begegnet dir früher oder später, wenn Anwendungen größer werden und trotzdem wartbar bleiben sollen. Der Name klingt komplizierter, als das Konzept tatsächlich ist. Im Kern geht es darum, Abhängigkeiten sauber zu trennen und dein fachliches Modell vor technischen Details zu schützen. Genau das ist im Alltag mit Java, WildFly, Maven und Git entscheidend, wenn ein Projekt nicht nach kurzer Zeit unübersichtlich werden soll.

 

Grundidee der Hexagonal Architecture

Die Hexagonal Architecture wird auch „Ports and Adapters“ genannt. Dieser Name beschreibt das Prinzip sehr gut. Deine Anwendung besteht aus einem fachlichen Kern. Dieser Kern kennt keine Datenbank, kein REST, kein Framework. Er kennt nur Fachlogik.

Alles Technische liegt außen herum und wird über klar definierte Schnittstellen angebunden. Diese Schnittstellen heißen Ports. Die konkrete technische Umsetzung heißt Adapter. Der Kern spricht nur mit Ports, niemals direkt mit Adaptern.

Das Ziel ist nicht, möglichst viele Schichten zu bauen. Das Ziel ist, Abhängigkeiten zu kontrollieren. Änderungen an der Technik sollen den fachlichen Kern möglichst nicht beeinflussen.

 

Der fachliche Kern

Im fachlichen Kern liegt deine eigentliche Logik. Hier definierst du Anwendungsfälle und Domänenobjekte. Der Code ist einfaches Java, ohne Annotationen von JPA, ohne REST-Frameworks, ohne WildFly-spezifische Klassen.

Ein einfaches Beispiel ist ein Anwendungsfall zum Anlegen eines Benutzers:

public class CreateUserService {

    private final UserRepository userRepository;

    public CreateUserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void create(String name) {
        User user = new User(name);
        userRepository.save(user);
    }
}

Diese Klasse weiß nichts darüber, wie ein Benutzer gespeichert wird. Sie kennt nur ein Interface. Genau das ist der Kern der Architektur.

 

Ports als Verträge

Ports sind Interfaces, die beschreiben, was der Kern von außen benötigt oder nach außen anbietet. Sie sind Verträge. Der Kern definiert diese Verträge selbst.

Das Repository aus dem Beispiel ist ein solcher Port:

public interface UserRepository {
    void save(User user);
}

Der Kern sagt damit: „Ich brauche eine Möglichkeit, Benutzer zu speichern.“ Wie das passiert, ist nicht seine Aufgabe.

 

Adapter für technische Details

Adapter sind konkrete Implementierungen der Ports. Hier kommen Datenbank, REST, Messaging oder andere technische Aspekte ins Spiel. Adapter hängen vom Kern ab, nicht umgekehrt.

Ein einfacher JPA-Adapter könnte so aussehen:

public class JpaUserRepository implements UserRepository {

    @Override
    public void save(User user) {
        // Persistieren mit JPA
    }
}

Diese Klasse darf Frameworks verwenden. Sie darf sich ändern, ohne dass du den fachlichen Kern anpassen musst.

 

Eingehende und ausgehende Seiten

Hexagonal Architecture unterscheidet nicht explizit zwischen Controller, Service und Repository. Stattdessen gibt es eingehende und ausgehende Adapter.

Eingehende Adapter rufen den Kern auf. Das kann ein REST-Endpunkt, ein CLI oder ein Test sein. Ausgehende Adapter werden vom Kern verwendet, zum Beispiel für Datenbanken oder externe Services.

Ein REST-Adapter könnte so aussehen:

@Path("/users")
public class UserResource {

    private final CreateUserService service;

    public UserResource(CreateUserService service) {
        this.service = service;
    }

    @POST
    public void create(String name) {
        service.create(name);
    }
}

Der REST-Code ist klar getrennt von der Fachlogik. Das macht den Code verständlicher und testbarer.

 

Testbarkeit als direkter Vorteil

Ein großer Vorteil dieser Architektur zeigt sich beim Testen. Da der Kern keine technischen Abhängigkeiten hat, kannst du ihn mit einfachen Unit-Tests prüfen.

Für Tests ersetzt du Adapter durch einfache Implementierungen:

UserRepository repo = user -> {};
CreateUserService service = new CreateUserService(repo);
service.create("Max");

Du brauchst keine Datenbank und keinen Application Server, um die Logik zu testen. Das spart Zeit und reduziert Fehler.

 

Einordnung im Java-Alltag

In Java-Projekten mit Maven und WildFly passt die Hexagonal Architecture gut, wenn du Module sauber trennst. Der Kern liegt oft in einem eigenen Maven-Modul ohne Abhängigkeiten zu Jakarta EE oder Spring.

Adapter-Module hängen vom Kern ab und bringen die technischen Abhängigkeiten mit. In Git sorgt diese Struktur dafür, dass Änderungen klar nachvollziehbar bleiben. In IntelliJ ist die Navigation einfacher, weil Verantwortlichkeiten klar verteilt sind.

 

Typische Fehler

Ein häufiger Fehler ist, Ports mit Framework-Annotationen zu versehen. Damit holst du Technik wieder in den Kern. Ein anderer Fehler ist, jede Kleinigkeit in einen Port zu pressen. Ports sollten sinnvoll und fachlich begründet sein.

Hexagonal Architecture ist kein Selbstzweck. Sie hilft, wenn sie bewusst eingesetzt wird. Kleine Anwendungen brauchen nicht zwingend diese Struktur. Größere profitieren deutlich davon.

 

Fazit

Hexagonal Architecture zwingt dich, über Abhängigkeiten nachzudenken. Der fachliche Kern bleibt klar und unabhängig. Technische Details sind austauschbar. Gerade in Java-Projekten mit längerer Lebensdauer ist das ein entscheidender Vorteil.

Wenn du das Prinzip einmal verstanden hast, wirkt der Code ruhiger und strukturierter. Änderungen werden planbarer, und Tests fühlen sich natürlicher an.