Softwarearchitektur: Layered, Hexagonal und Clean Architecture kurz verglichen
Softwarearchitektur beginnt nicht beim Framework.
Frameworks geben Struktur vor. Sie legen aber nicht fest, welche Logik wohin gehört, wie Datenbankoperationen aufgebaut sein müssen und welche Regeln im Projekt gelten.
Solange ein Projekt überschaubar ist, läuft vieles rund.
Controller, Service, Repository, Datenbank.
Das reicht für den Start oft aus.
Schwieriger wird es, wenn das System wächst.
Plötzlich verteilt sich Logik in Controllern, Services verantworten mehrere Themen und Datenbankzugriffe erfolgen wild an unterschiedlichen Stellen.
Es entsteht schwer wartbarer Code und technische Schuld.
Dann ist nicht das Framework das Problem.
Das Problem sind fehlende Architekturregeln.
1. Layered Architecture
Layered Architecture ist der klassische Schichtenaufbau.
Controller
-> Service
-> Repository
-> Database
Beispiel Rechnung erstellen:
InvoiceController.create(request)
-> invoiceService.createInvoice(customerId, items)
InvoiceService.createInvoice(customerId, items)
customer = customerRepository.find(customerId)
invoice = new Invoice(customer, items)
invoiceRepository.save(invoice)
| Punkt | Bewertung |
|---|---|
| Passt gut für | kleine Anwendungen, CRUD-Anwendungen, interne Tools, überschaubare Logik, kleine Teams |
| Pro | einfach verständlich, schnell umgesetzt, vielen Entwicklern bekannt, für einfache Anwendungen oft ausreichend |
| Contra | Services werden schnell zum Sammelpunkt, Logik landet im Controller, Datenbankzugriffe verteilen sich, Tests werden schwieriger |
Wichtige Regeln
| ✅ Gut | ❌ Kritisch |
|---|---|
| Controller enthalten keine oder nur minimale Logik, zum Beispiel Auth-Prüfung oder Request-Validierung. | Controller entscheiden über mehrere Services per if/else und steuern dadurch den Ablauf. |
| Services enthalten den Anwendungsablauf und koordinieren die notwendigen Schritte. | Services werden zum Sammelbecken für Berechtigungen, Berechnungen, Datenbankzugriffe, API-Aufrufe und Sonderfälle. |
| Datenbankzugriffe laufen klar gekapselt über Repositories oder vergleichbare Datenzugriffsklassen. | SQL oder ORM-Zugriffe tauchen in Controllern, Services, Helpern oder Jobs verteilt auf. |
| Regeln sind an einer nachvollziehbaren Stelle gebündelt. | Die gleiche Regel wird an mehreren Stellen ähnlich, aber nicht identisch umgesetzt. |
| Tests können Services oder Anwendungslogik ohne komplette Oberfläche prüfen. | Tests funktionieren nur, wenn Webserver, Datenbank und weitere Infrastruktur vollständig laufen. |
2. Hexagonal Architecture
Hexagonal Architecture wird auch Ports-and-Adapters genannt.
Die Grundidee:
Die Logik steht in der Mitte.
Framework, Datenbank und Schnittstellen liegen außen.
REST Adapter
CLI Adapter
Message Adapter
-> Use Case
-> Port
-> Database Adapter
-> External API Adapter
Beispiel Rechnung erstellen:
RestInvoiceAdapter
-> CreateInvoiceUseCase
-> CustomerPort
-> InvoicePort
-> NotificationPort
CreateInvoiceUseCase.execute(command)
customer = customerPort.getById(command.customerId)
invoice = Invoice.create(customer, command.items)
invoicePort.save(invoice)
notificationPort.sendInvoiceCreated(invoice)
Die technische Umsetzung sitzt außen.
SqlInvoiceAdapter implements InvoicePort
MailNotificationAdapter implements NotificationPort
RestInvoiceController calls CreateInvoiceUseCase
| Punkt | Bewertung |
|---|---|
| Passt gut für | wachsende Anwendungen, externe Schnittstellen, Messaging, Dateiimporte, Dateiexporte, Legacy-Migration, hohe Testanforderung |
| Pro | Use Cases enthalten den eigentlichen Ablauf, Datenbank, Schnittstellen oder Mailversand lassen sich besser austauschen, Tests werden einfacher, mehrere Eingänge können denselben Use Case verwenden |
| Contra | mehr Struktur, mehr Schnittstellen, mehr Benennung, für kleine Anwendungen schnell zu viel |
Wichtige Regeln
| ✅ Gut | ❌ Kritisch |
|---|---|
| Use Cases beschreiben einen klaren Anwendungsfall. | Use Cases werden zu allgemeinen Service-Klassen ohne klare Aufgabe. |
| Die innere Logik arbeitet gegen Ports/Schnittstellen. | Die innere Logik kennt konkrete Datenbank-, HTTP- oder Mail-Implementierungen. |
| Adapter enthalten technische Details wie REST, SQL, Mail oder Messaging. | Technische Details wandern in den Use Case. |
| Mehrere Eingänge können denselben Use Case verwenden. | REST, CLI und Messaging enthalten jeweils eigene Logik. |
| Tests prüfen den Use Case mit Fakes oder Mocks. | Tests brauchen echte Datenbank, echten Mailserver oder externe Systeme. |
3. Clean Architecture
Clean Architecture geht noch einen Schritt weiter.
Die Abhängigkeiten zeigen konsequent nach innen.
Framework
-> Interface Adapter
-> Use Case
-> Entity
Beispiel Rechnung erstellen:
CreateInvoiceInteractor.execute(input)
invoice = InvoiceEntity.create(input.customerId, input.items)
invoiceGateway.save(invoice)
outputPresenter.present(invoice)
Im Zentrum stehen Regeln und Anwendungsfälle.
Framework, Datenbank, Web, JSON, HTML oder externe Systeme liegen außen.
| Punkt | Bewertung |
|---|---|
| Passt gut für | langlebige Kernsysteme, komplexe Logik, mehrere Teams, hohe Testanforderung |
| Pro | klare Abhängigkeitsrichtung, Logik ist klar von Framework, Datenbank und Oberfläche getrennt, Framework spielt eine kleinere Rolle, hohe Testbarkeit |
| Contra | hoher Strukturaufwand, viele Begriffe, höhere Einstiegshürde, für einfache Anwendungen oft zu schwer |
Wichtige Regeln
| ✅ Gut | ❌ Kritisch |
|---|---|
| Abhängigkeiten zeigen nach innen. | Innere Schichten kennen Framework, Datenbank oder Webdetails. |
| Use Cases beschreiben konkrete Anwendungsfälle. | Use Cases werden zu technischen Service-Sammlungen. |
| Entities enthalten zentrale Regeln. | Entities sind nur Datencontainer ohne Bedeutung für die Logik. |
| Adapter übersetzen zwischen Technik und Kern. | Controller, Presenter oder Gateways enthalten Entscheidungen zum Ablauf. |
| Die Architektur wird nur dort eingesetzt, wo der Aufwand gerechtfertigt ist. | Clean Architecture wird für einfache CRUD-Masken durchgezogen, obwohl kein Nutzen entsteht. |
4. Vergleich
| Kriterium | Layered | Hexagonal | Clean |
|---|---|---|---|
| Einstieg | einfach | mittel | anspruchsvoll |
| Strukturaufwand | gering | mittel | hoch |
| Testbarkeit | mittel | hoch | hoch |
| Gut für CRUD | ja | teilweise | eher selten |
| Externe Systeme | begrenzt | gut | gut |
| Legacy-Migration | begrenzt | sehr gut | gut, aber aufwendiger |
| Agentic Coding Workflows | möglich, aber klare Regeln nötig | gut geeignet durch klare Use Cases, Ports und Adapter | gut geeignet, aber nur bei sauber verstandener Struktur |
| Risiko Overengineering | gering | mittel | hoch |
Agentic Coding braucht klare Regeln, wie Code zu schreiben ist.
Das gilt unabhängig von der Architektur.
Je klarer die Architektur abgegrenzt ist, desto eher ergeben sich diese Regeln direkt aus der Projektstruktur.
Bei Layered Architecture müssen Grenzen oft zusätzlich beschrieben werden.
Bei Hexagonal oder Clean Architecture sind viele Grenzen bereits sichtbarer: Use Case, Port, Adapter, Entity oder äußere Schicht.
5. Entscheidungshilfe
| Situation | Empfehlung |
|---|---|
| Kleine Anwendung mit wenig Logik | Layered Architecture |
| Klassische CRUD-Anwendung | Layered Architecture |
| Wachsende Anwendung mit mehreren Schnittstellen | Hexagonal Architecture |
| Legacy-System soll schrittweise modernisiert werden | Hexagonal Architecture |
| Langlebiges Kernsystem mit komplexer Logik | Clean Architecture oder strenge Hexagonal Architecture |
| Team kennt die Architektur nicht sicher | lieber einfacher starten und Regeln sauber einhalten |
Typische Fehler
| Fehler | Problem |
|---|---|
| Logik im Controller | Controller werden schwer testbar und schwer wartbar |
| Services als Sammelbecken | Zuständigkeiten verschwimmen |
| Datenbankzugriffe an mehreren Stellen | Änderungen am Datenmodell werden teuer |
| Externe Systeme direkt im Service | Tests und Austauschbarkeit werden schwieriger |
| Architektur nur als Ordnerstruktur | Der Code sieht sauber aus, ist aber trotzdem gekoppelt |
| Tests nur mit kompletter Infrastruktur | Fehler werden spät gefunden und Änderungen werden riskanter |
Auf den Punkt
Layered Architecture ist einfach und oft ausreichend.
Hexagonal Architecture schützt die Logik besser vor Framework, Datenbank und Schnittstellen.
Clean Architecture trennt am konsequentesten, ist aber nicht für jedes Projekt notwendig.
Je größer das Risiko durch Änderungen, Tests und Wartung ist, desto klarer muss die Architektur sein.
Gute Architektur ist kein Selbstzweck.
Sie soll Änderungen einfacher machen, nicht komplizierter.