Clean Architecture: Schichten, Grenzen und die Frage, was man weglässt

Clean Architecture: Schichten, Grenzen und die Frage, was man weglässt

Clean Architecture: Schichten, Grenzen und die Frage, was man weglässt

Artikel-Übersicht

Clean Architecture, Hexagonal Architecture und Onion Architecture sind drei Namen für weitgehend dieselbe Idee, und die Idee lässt sich in einem Satz sagen. Dieser Artikel beginnt mit dieser einen Regel und arbeitet heraus, was aus ihr folgt: dass Datenbank und Web-Framework Details sind und keine Grundlage. Danach löst er die Verwechslung auf, die in der Suche am häufigsten auftaucht — Clean Code ist Handwerk auf Zeilenebene, Clean Architecture ist Struktur auf Systemebene, und die beiden Bücher lösen verschiedene Probleme. Es folgt der ehrliche Vergleich der drei Modelle, ihre Herkunft mit korrekten Jahreszahlen, und dann der konkrete Teil: eine vollständige Ordnerstruktur mit den erlaubten Abhängigkeiten und der Weg, diese Regel maschinell zu prüfen, statt sich auf Disziplin zu verlassen. Zum Schluss der Abschnitt, den kein Lehrbuch hat — was man in echten Projekten weglassen kann, und was man nie weglässt.

Was Clean Architecture ist

Clean Architecture ist ein Strukturprinzip, bei dem Abhängigkeiten ausschließlich nach innen zeigen — auf die Geschäftslogik, nie von ihr weg.

Das ist die ganze Regel. Alles, was in diesem Artikel folgt, ist eine Konsequenz daraus.

Die Konsequenz, die im Alltag am meisten überrascht: Die Datenbank ist ein Detail. Das Web-Framework ist ein Detail. Die Benutzeroberfläche ist ein Detail. Sie sind austauschbar, weil nichts im Kern von ihnen abhängt. Das ist keine Geringschätzung — Details sind wichtig und aufwendig. Es ist eine Aussage über die Richtung der Abhängigkeit.

In den meisten Projekten ist es umgekehrt. Da beginnt der Entwurf beim Datenbankschema, die Geschäftslogik hängt an einem ORM-Objekt, und die Fachklassen kennen die Framework-Annotationen. Das funktioniert — bis die Datenbank gewechselt werden soll, bis das Framework eine grundlegend andere Hauptversion bekommt, oder bis derselbe Ablauf zusätzlich über eine Schnittstelle statt über ein Formular aufgerufen werden muss.

Die Schichten von innen nach außen:

Schicht Enthält Kennt
Entitäten fachliche Regeln, die unabhängig von jeder Anwendung gelten nichts
Anwendungsfälle die Abläufe, die deine Anwendung anbietet Entitäten
Adapter Übersetzung zwischen innen und außen: Controller, Repository-Implementierungen, Präsentation Anwendungsfälle, Entitäten
Infrastruktur Framework, Datenbank, Netzwerk, Dateisystem alles darüber

Die rechte Spalte ist die eigentliche Aussage. Jede Zeile kennt nur, was über ihr steht — niemals, was darunter liegt.

Clean Architecture ist nicht Clean Code

Diese Verwechslung ist so verbreitet, dass Google sie in die Suchergebnisse mischt: Wer nach Clean Architecture sucht, bekommt Fragen zu DRY-Prinzip, Refactoring und „was sind gute Codes" eingeblendet.

Die beiden Begriffe beschreiben verschiedene Ebenen:

Clean Code Clean Architecture
Ebene Zeilen, Funktionen, Klassen Module, Schichten, Grenzen
Worum es geht Lesbarkeit, Benennung, Funktionslänge, Duplikate Abhängigkeitsrichtung, Austauschbarkeit, Trennung von Fachlichkeit und Technik
Typische Frage „Ist diese Funktion zu lang?" „Darf dieses Paket dieses andere kennen?"
Wirkung Der Code liest sich besser Das System bleibt änderbar
Buch Clean Code (2008) Clean Architecture (2017)

Beides stammt vom selben Autor, und beides ist nützlich — aber sie ersetzen einander nicht. Sauber benannte Funktionen in einem System, dessen Geschäftslogik am ORM klebt, machen den Umzug auf eine andere Datenbank keinen Tag kürzer. Und umgekehrt: Eine saubere Schichtung mit unleserlichen Funktionen ist trotzdem unangenehm zu warten.

DRY und Refactoring gehören zur ersten Ebene. Wann Refactoring lohnt und wie man technische Schulden sichtbar macht, ist ein eigenes Thema — hier geht es um die Struktur darüber.

Hexagonal, Onion, Clean — was unterscheidet sie wirklich?

Die kurze Antwort, die selten so klar gesagt wird: Die Abhängigkeitsregel ist bei allen dreien identisch. Die Unterschiede liegen in der Terminologie und darin, wie explizit die Ein- und Ausgänge modelliert werden.

Modell Jahr, Urheber Zentrale Metapher Ein-/Ausgänge Besonderheit
Hexagonal (Ports and Adapters) 2005, Alistair Cockburn ein Sechseck mit Anschlüssen ringsum explizit als Ports modelliert betont die Symmetrie: eingehend und ausgehend sind gleichwertig
Onion 2008, Jeffrey Palermo konzentrische Ringe implizit über Schnittstellen betont die Schichtung und die Abhängigkeitsumkehr
Clean 2012/2017, Robert C. Martin konzentrische Ringe mit benannten Schichten über Schnittstellen an der Grenze benennt die Schichten und gibt eine konkrete Aufteilung vor
(zum Vergleich) klassische Schichtenarchitektur älter Stapel von oben nach unten keine Abhängigkeit zeigt nach unten zur Datenbank — genau das Gegenteil

Die letzte Zeile ist die wichtige. Der Unterschied zwischen Clean Architecture und der klassischen Dreischichtung ist nicht die Anzahl der Schichten, sondern die Richtung: Klassisch hängt die Geschäftslogik an der Datenzugriffsschicht. Bei Clean Architecture definiert die Geschäftslogik eine Schnittstelle, und die Datenzugriffsschicht implementiert sie.

Wo die fachlichen Grenzen verlaufen, an denen man überhaupt schneidet, ist eine eigene Frage — sie gehört zu Domain-Driven Design, das den fachlichen Schnitt liefert, während Clean Architecture die technische Struktur beisteuert.

Der Mechanismus dahinter heißt Abhängigkeitsumkehr: Nicht das Innere ruft das Äußere direkt auf, sondern das Innere definiert, was es braucht, und das Äußere erfüllt es. Wer schon mit einem Framework gearbeitet hat, das Objekte injiziert, hat das Prinzip bereits benutzt — nur vielleicht nicht bewusst.

Praktisch heißt das: Wenn du dich zwischen den drei Modellen entscheiden musst, entscheidest du über Vokabular, nicht über Architektur. Nimm das, dessen Begriffe dein Team versteht.

Woher die Begriffe kommen

Kurz, weil die Zuschreibungen im Netz durchgängig durcheinandergehen:

Alistair Cockburn beschrieb 2005 die Hexagonal Architecture, auch Ports and Adapters. Das Sechseck hat keine Bedeutung — es sollte nur verhindern, dass man an „oben" und „unten" denkt.

Jeffrey Palermo prägte 2008 den Begriff Onion Architecture in einer Blogreihe, mit stärkerem Gewicht auf der Schichtung.

Robert C. Martin veröffentlichte 2012 den Blogbeitrag zur Clean Architecture und 2017 das gleichnamige Buch. Sein Beitrag war weniger die Idee als die Zusammenführung: Er benannte die Vorläufer und leitete daraus eine gemeinsame Regel ab.

Alle drei bauen auf demselben Gedanken auf, den Ivar Jacobson schon in den frühen Neunzigern formuliert hatte.

Wie das im Projekt aussieht

Hier hören die meisten Artikel auf, und hier fangen die Fragen an. Eine Ordnerstruktur, die man übernehmen kann:

src/
├── domain/                      ← Entitäten: kennt NICHTS
│   ├── model/
│   │   ├── Rechnung.java
│   │   └── Rechnungsnummer.java
│   └── service/
│       └── Rechnungspruefung.java
│
├── application/                 ← Anwendungsfälle: kennt domain
│   ├── port/
│   │   ├── in/
│   │   │   └── RechnungFreigeben.java        (Schnittstelle)
│   │   └── out/
│   │       ├── RechnungRepository.java       (Schnittstelle)
│   │       └── BenachrichtigungVersenden.java (Schnittstelle)
│   └── usecase/
│       └── RechnungFreigebenService.java
│
├── adapter/                     ← kennt application + domain
│   ├── in/
│   │   ├── web/
│   │   │   └── RechnungController.java
│   │   └── messaging/
│   │       └── RechnungEventListener.java
│   └── out/
│       ├── persistence/
│       │   ├── RechnungJpaEntity.java
│       │   └── RechnungPersistenceAdapter.java
│       └── mail/
│           └── MailAdapter.java
│
└── infrastructure/              ← Konfiguration, Framework-Verdrahtung
    └── config/

Drei Dinge, die diese Struktur transportiert:

Die Ports liegen innen, die Adapter außen. RechnungRepository ist eine Schnittstelle in application/port/out — definiert von der Anwendung, implementiert vom Persistenz-Adapter. Das ist die Abhängigkeitsumkehr in Ordnerform.

RechnungJpaEntity ist nicht Rechnung. Zwei Klassen, zwei Zwecke: eine fachliche und eine für die Datenbank. Das ist der Punkt, an dem am häufigsten abgekürzt wird — dazu unten mehr.

Die Richtung ist am Import erkennbar. Ein Import von adapter in domain ist ein Regelbruch. Und genau den kann man prüfen lassen.

Die Regel maschinell durchsetzen

Eine Architekturregel, die nur in einem Dokument steht, ist nach zwei Jahren nicht mehr wahr. Sie gehört in den Testlauf:

@AnalyzeClasses(packages = "de.beispiel.rechnung")
class ArchitekturTest {

    @ArchTest
    static final ArchRule domaene_kennt_niemanden =
        noClasses().that().resideInAPackage("..domain..")
            .should().dependOnClassesThat()
            .resideInAnyPackage("..application..", "..adapter..", "..infrastructure..");

    @ArchTest
    static final ArchRule anwendung_kennt_keine_adapter =
        noClasses().that().resideInAPackage("..application..")
            .should().dependOnClassesThat()
            .resideInAnyPackage("..adapter..", "..infrastructure..");
}

Das sind zwei Regeln und zehn Zeilen. Sie bewirken mehr als jedes Architekturdokument, weil ein Verstoß den Testlauf rot färbt statt eine Diskussion im Review auszulösen. Für andere Sprachen gibt es entsprechende Werkzeuge; das Prinzip ist überall dasselbe.

In anderen Sprachen und Umgebungen

Android hat Clean Architecture früh übernommen, meist mit den drei Modulen domain, data und presentation. Die Besonderheit: Der Lebenszyklus der Oberfläche drängt Zustand nach innen — dagegen hilft, ViewModels konsequent in der Präsentationsschicht zu halten.

Go löst die Sache anders. Weil Schnittstellen implizit erfüllt werden, kann die Schnittstelle dort stehen, wo sie gebraucht wird, statt wo sie implementiert wird. Das ist Abhängigkeitsumkehr ohne Zeremonie und einer der Gründe, warum Go-Projekte oft flacher aussehen, ohne die Regel zu verletzen.

TypeScript braucht Disziplin bei den Importpfaden. Ohne Modulgrenzen des Werkzeugs ist ein Import aus adapter in domain genauso leicht wie jeder andere. Hier lohnt eine Lint-Regel, die Importpfade prüft.

Was man weglassen kann

Der Abschnitt, den kein Lehrbuch hat — und der aus zwei Jahren mit einer Vollform mehr wert ist als das Kapitel über Schichten.

Ein Interface je Repository, das genau eine Implementierung hat, ist Zeremonie. Die Rechtfertigung lautet „damit wir die Datenbank tauschen können". In den allermeisten Projekten wird sie nie getauscht. Die Schnittstelle lohnt trotzdem — aber nicht wegen der Austauschbarkeit, sondern weil sie die Abhängigkeitsrichtung umdreht und Tests ohne Datenbank ermöglicht. Wenn beides nicht gebraucht wird, ist sie weg.

Getrennte Modelle je Schicht lohnen erst ab einer bestimmten Größe. Fachliches Modell, Datenbankentität, Übertragungsobjekt für die Schnittstelle: In einem kleinen System sind das dreimal dieselben Felder und zwei Abbildungen, die bei jeder Änderung mitgepflegt werden müssen. Die Trennung zahlt sich erst, wenn die Modelle tatsächlich auseinanderlaufen — wenn die Datenbank Felder hat, die fachlich nicht existieren, oder die Schnittstelle Felder braucht, die das Fachmodell nicht kennt. Bis dahin ist ein gemeinsames Modell die ehrlichere Lösung.

Use-Case-Klassen für reine Durchreicher sind Ballast. Ein Anwendungsfall, der nichts tut, außer das Repository aufzurufen und das Ergebnis weiterzugeben, ist eine Datei mehr ohne Erkenntnisgewinn. Für CRUD-Operationen ohne fachliche Regel darf der Adapter direkt auf das Repository zugreifen.

Und was man nie weglässt: die Abhängigkeitsrichtung. Jede der obigen Vereinfachungen ist zurücknehmbar, solange nichts aus dem Kern nach außen zeigt. Ist diese Regel einmal gebrochen — hängt also das Fachmodell an einer Framework-Annotation oder an einer Datenbankklasse —, kostet die Korrektur ein Vielfaches.

Wann Clean Architecture die falsche Wahl ist

Vier Fälle, in denen der Aufwand den Nutzen übersteigt:

  • Kurze Lebensdauer. Ein Prototyp, der in drei Monaten weggeworfen wird, braucht keine austauschbare Infrastruktur.
  • Dünne Fachlichkeit. Wenn die Anwendung im Kern Formulare auf Tabellen abbildet, gibt es keine Geschäftslogik, die man schützen müsste.
  • Ein einziger Eingangskanal, der sich nicht ändert. Der Nutzen wächst mit der Zahl der Wege in dieselbe Logik hinein.
  • Ein Team, das die Regel nicht mitträgt. Eine halb umgesetzte Schichtung ist schlechter als gar keine, weil sie Ordnung vortäuscht.

Und der Gegenfall, in dem sie sich fast immer auszahlt: lange Lebensdauer, wechselnde Infrastruktur, mehrere Eingangskanäle — Weboberfläche, Schnittstelle, geplanter Auftrag, Ereignisverarbeitung — auf denselben Ablauf.

Ein Beispiel aus dem KI-Umfeld, weil es die Regel gut zeigt: Wer ein Sprachmodell in eine Anwendung einbaut, sollte den Modellzugriff als Port modellieren und den konkreten Anbieter als Adapter. Modelle und Anbieter wechseln derzeit im Halbjahrestakt — das ist die Sorte Detail, für die die Regel gemacht ist. Wie schnell sich dieses Feld bewegt, sieht man im Vergleich der führenden LLM-Anbieter; dass derselbe Ablauf auch gegen ein lokales Modell laufen kann, ist dann eine Frage der Konfiguration statt einer Umbaumaßnahme.

Häufige Fragen

Was ist Clean Architecture? Ein Strukturprinzip, bei dem Abhängigkeiten ausschließlich nach innen zeigen — auf die Geschäftslogik. Datenbank, Framework und Oberfläche sind Details, die von außen an den Kern andocken.

Was ist der Unterschied zwischen Clean Code und Clean Architecture? Clean Code betrifft Zeilen, Funktionen und Benennung. Clean Architecture betrifft Module, Schichten und die Richtung der Abhängigkeiten. Zwei Ebenen, zwei Bücher, kein Ersatz füreinander.

Was unterscheidet Hexagonal, Onion und Clean Architecture? Die Abhängigkeitsregel ist bei allen dreien dieselbe. Unterschiedlich sind die Begriffe und wie explizit die Ein- und Ausgänge modelliert werden. Die Wahl ist eine Vokabularentscheidung.

Was sind die Ziele einer Softwarearchitektur? Ein System änderbar zu halten, ohne es zu verstehen zu müssen. Konkret: Entscheidungen so anordnen, dass die teuren möglichst spät fallen und die häufigen möglichst lokal bleiben.

Wie viele Schichten braucht man? So wenige wie möglich. Vier sind eine gängige Aufteilung, drei reichen häufig. Entscheidend ist nicht die Zahl, sondern dass keine Abhängigkeit nach außen zeigt.

Wie stellt man sicher, dass die Regel eingehalten wird? Maschinell. Zwei Architekturregeln im Testlauf bewirken mehr als jedes Dokument, weil ein Verstoß den Build rot färbt.

Zusammenfassung und praktische Tipps

Clean Architecture lässt sich auf eine Regel zusammenziehen, und der Rest sind Konsequenzen daraus. Die wichtigsten Punkte:

  • Abhängigkeiten zeigen nach innen. Alles andere folgt daraus, und nur diese Regel darf man nie brechen.
  • Clean Code ist nicht Clean Architecture. Verschiedene Ebenen, verschiedene Probleme.
  • Hexagonal, Onion und Clean sind dieselbe Idee. Die Wahl betrifft das Vokabular, nicht die Struktur.
  • Der Unterschied zur klassischen Schichtung ist die Richtung, nicht die Anzahl der Schichten.
  • Prüfe die Regel maschinell. Zehn Zeilen Architekturtest schlagen jedes Dokument.
  • Getrennte Modelle je Schicht lohnen erst, wenn sie auseinanderlaufen. Bis dahin sind sie doppelte Pflege.
  • Ein Interface mit genau einer Implementierung ist nur dann sinnvoll, wenn es die Abhängigkeit umdreht oder Tests ermöglicht.

Mein Rat für den Einstieg: Fang nicht mit der Ordnerstruktur an, sondern mit den zwei Architekturtests. Sie zwingen dich, die Grenzen zu benennen, bevor du sie baust — und sie sagen dir nach jedem Commit, ob die Struktur noch stimmt. Alles andere kannst du danach schrittweise nachziehen, ohne dass dir jemand zwischendurch die Richtung verdreht.

Ressourcen