Clean Architecture
Anwendungsfälle im Zentrum, Technik am Rand
Die Idee und ihr Nutzen
In einer Versicherung gibt eine Sachbearbeiterin Schäden im internen Schadenportal zur Zahlung frei. Künftig soll das auch über eine App möglich sein, eine mobile Anwendung für Mitarbeitende. In beiden Fällen gilt dieselbe Regel: Liegt ein Schaden über 10.000 Euro, muss eine zweite Person freigeben, weil bei so hohen Zahlungen ein Fehler oder ein Betrug nicht unbemerkt bleiben soll. Diese Prüfung steht deshalb in einem eigenen Programmteil „Schaden freigeben“, den sowohl das Schadenportal als auch die App aufruft. Der Programmteil legt auch fest, welche Daten er zum Laden und Speichern eines Schadens braucht, etwa Betrag und Bearbeitungsstand. Ein gesonderter Programmteil, der Datenbankadapter, setzt diese Anfragen in Befehle für die Datenbank um. So steht die Freigaberegel nur in einem Programmteil, auch wenn später ein weiterer Zugang wie ein automatischer Import oder eine andere Datenbank hinzukommt. Diese Bauweise heißt Clean Architecture, auf Deutsch saubere Architektur.
Wie funktioniert es?
Die Sachbearbeiterin Frau Meier klickt im Schadenportal beim Schadenfall 4711 auf „Freigeben“. Das Schadenportal gibt den Klick an den Portal-Adapter weiter, einen kleinen Programmteil, der Eingaben aus dem Schadenportal für die Freigaberegel übersetzt. Der Portal-Adapter macht aus dem Klick einen Aufruf des Anwendungsfalls „Schaden freigeben“ mit der Fallnummer 4711 und Frau Meiers Kennung. Der Anwendungsfall „Schaden freigeben“ ist der Programmteil mit der Freigaberegel. Er prüft zuerst, ob Frau Meier überhaupt freigeben darf. Dann ruft der Anwendungsfall über die Speicherschnittstelle, die er selbst festgelegt hat, „Fall laden“ für Fall 4711 auf. Der Anwendungsfall spricht die Datenbank nicht selbst an. Erst der Datenbankadapter setzt „Fall laden“ in einen Lesebefehl für die tatsächliche Datenbank um. Die Datenbank liefert Fall 4711 mit 12.000 Euro an den Datenbankadapter, und der Datenbankadapter übergibt den Fall in der Form, die der Anwendungsfall erwartet. Der Anwendungsfall wendet die Regel an: 12.000 Euro liegen über der Grenze von 10.000 Euro. Der Anwendungsfall setzt den Stand deshalb auf „zweite Freigabe ausstehend“ und lässt den neuen Stand über die Speicherschnittstelle speichern; der Datenbankadapter schreibt den Stand in die Datenbank. Dann meldet der Anwendungsfall das Ergebnis an den Portal-Adapter, und das Schadenportal zeigt Frau Meier: „Eine zweite Freigabe ist erforderlich.“ Ein halbes Jahr später gibt Frau Meier einen Schaden über 15.000 Euro unterwegs in der App frei. Der App-Adapter ruft denselben Anwendungsfall auf, und dieselbe Regel greift.
Clean Architecture trennt vier Aufgaben: Fachregeln, Anwendungsfälle, Zugänge und Speicherung. Die Fachregeln beschreiben, was für jeden Schadenfall gilt, egal in welchem Vorgang: Ein Schadenfall hat einen Betrag und einen Bearbeitungsstand, und ein bereits bezahlter Schadenfall darf nicht noch einmal freigegeben werden. Ein Anwendungsfall ist ein fachlicher Vorgang, den ein Mensch auslöst. In der Clean Architecture ist jeder Anwendungsfall ein eigener Programmteil. Der Anwendungsfall „Schaden freigeben“ entscheidet, ob ein Fall freigegeben ist oder noch eine zweite Freigabe braucht. Die Zugänge Schadenportal und App nehmen Eingaben entgegen und zeigen Ergebnisse an. Jeder Zugang hat einen eigenen Adapter, der Eingaben in Aufrufe des Anwendungsfalls übersetzt und das Ergebnis an den Zugang zurückgibt. Das Schadenportal hat den Portal-Adapter, die App den App-Adapter. Für die Speicherung legt der Anwendungsfall eine Speicherschnittstelle fest, also eine Vereinbarung, welche Vorgänge der Programmteil für die Speicherung anbieten muss. Hier sind das „Fall laden“ und „Fall speichern“. Der Datenbankadapter bietet diese beiden Vorgänge an und setzt sie in Befehle für die tatsächlich verwendete Datenbank um. Nur der Anwendungsfall ruft die beiden Vorgänge auf.
Der Grundsatz lautet: Anwendungsfall und Fachregeln dürfen weder Code der Oberfläche noch Code der Datenbank aufrufen oder verwenden. Ein Programmteil hängt von einem anderen ab, wenn sein Code den anderen aufruft oder dessen Vereinbarungen verwendet. Portal-Adapter und App-Adapter hängen also vom Anwendungsfall ab, und der Datenbankadapter hängt von der Speicherschnittstelle ab, die der Anwendungsfall festgelegt hat. Alle Abhängigkeiten zeigen damit zur Fachlogik, also zu Anwendungsfall und Fachregeln, und nie von ihr weg. Das meint der Titel „Anwendungsfälle im Zentrum, Technik am Rand“. Robert C. Martin hat die Bauweise 2012 beschrieben und zeichnet diese Aufteilung als ineinanderliegende Kreise, mit den Fachregeln innen und den Oberflächen und Datenbanken außen. Welcher Datenbankadapter die Speicherschnittstelle erfüllt, wird beim Programmstart festgelegt. Für Tests der Regel verwendet das Entwicklungsteam statt des Datenbankadapters einen Ersatz, der die Schadenfälle nur im Arbeitsspeicher hält und dieselbe Speicherschnittstelle erfüllt. So läuft der Test in Sekunden ohne Datenbank. Clean Architecture garantiert allerdings nicht, dass jeder Zugang den Anwendungsfall auch tatsächlich nutzt. Die Bauweise legt außerdem nicht fest, welche Datenbank oder welche Oberfläche verwendet wird; sie sorgt nur dafür, dass bei einem Wechsel der Datenbank nur der Datenbankadapter neu geschrieben wird und der Anwendungsfall unverändert bleibt.
Clean Architecture: dieselbe Freigaberegel für Portal und App
Wann ist es sinnvoll und wann nicht?
Clean Architecture lohnt sich
- Eine Anwendung hat viele Fachregeln und mehrere Zugänge. Zum Schadenportal kommen die App und später vielleicht ein automatischer Import hinzu. Prüft das Schadenportal die Grenze von 10.000 Euro nur in dem Code, der beim Klick auf „Freigeben“ läuft, muss jemand für jeden neuen Zugang daran denken, die Prüfung noch einmal zu programmieren. Vergisst er es oder programmiert er sie leicht anders, kann über den neuen Zugang ein Schaden über 10.000 Euro ohne zweite Freigabe bezahlt werden. Mit Clean Architecture braucht ein neuer Zugang nur einen eigenen Adapter.
- Fachregeln sollen ohne laufendes Schadenportal und ohne echte Datenbank getestet werden. Steht die Regel mitten im Code, der mit der Datenbank arbeitet, braucht jeder Test eine laufende Datenbank mit passenden Testdaten. Dabei soll nur die Frage „über oder unter 10.000 Euro?“ geprüft werden.
- Die Anwendung soll viele Jahre im Einsatz bleiben, und Datenbank oder Oberfläche werden in dieser Zeit voraussichtlich ausgetauscht. Dasselbe gilt für verwendete Programmbibliotheken, also fertigen Code anderer Hersteller. Oberflächen und Datenbanken ändern sich aus anderen Gründen und zu anderen Zeitpunkten als die Freigaberegel. Wechselt die Versicherung ihre Datenbank, schreibt das Entwicklungsteam einen neuen Datenbankadapter, und Anwendungsfall und Fachregeln bleiben unverändert.
Clean Architecture ist nicht das Richtige
- Eine kleine Anwendung zeigt Daten nur an, speichert sie und trifft kaum fachliche Entscheidungen. Ein Beispiel ist eine Liste der Sachbearbeiterinnen mit ihren Telefonnummern. Adapter und Speicherschnittstelle würden die Daten dann nur unverändert weiterreichen, denn es gibt keine Regel, die von Oberfläche und Datenbank getrennt bleiben müsste. Stattdessen baut das Entwicklungsteam die Anwendung als Layered Architecture: Die Oberfläche ruft die Fachlogik direkt auf, und die Fachlogik ruft den Datenzugriff direkt auf. Ein eigener Adapter für jeden Zugang und eine Speicherschnittstelle entfallen. Dafür hängt dort die Fachlogik vom Datenzugriff ab; Clean Architecture kehrt diese Richtung um.
- Ein Programm wird nur kurz gebraucht und danach nicht weiterentwickelt. Ein Beispiel ist ein Prototyp für eine Vorführung. Dann schreibt das Entwicklungsteam Regel und Datenbankzugriff zusammen in einen Programmteil. Adapter und Speicherschnittstelle, die erst bei späteren Änderungen Arbeit sparen, entfallen.
- Eine Auswertung soll die Datenbank selbst rechnen lassen. Ein Beispiel ist die Summe aller offenen Schäden über Millionen Datensätze. Über die Speicherschnittstelle mit „Fall laden“ und „Fall speichern“ müsste der Anwendungsfall jeden der Millionen Schadenfälle einzeln holen und die Beträge selbst zusammenzählen. Stattdessen fragt das Entwicklungsteam die Summe mit einer einzigen Abfrage direkt bei der Datenbank ab, und die Datenbank rechnet sie selbst aus.
- Die Anwendung hat nur einen Anwendungsfall wie „Schaden freigeben“, und keine Fachregel wird in einem zweiten Anwendungsfall gebraucht. Getrennte Programmteile für Fachregeln und Anwendungsfälle würden dann nur trennen, was immer zusammen geändert wird. Stattdessen nutzt das Entwicklungsteam die Hexagonal Architecture. Die Hexagonal Architecture trennt die Anwendung ebenfalls über Schnittstellen von Oberflächen und Datenbanken, unterscheidet innerhalb der Anwendung aber nicht zwischen Fachregeln und Anwendungsfällen. Die Aufteilung in getrennte Programmteile für Fachregeln und Anwendungsfälle entfällt.
Nachteile und typische Fehler
Jeder Zugang braucht einen eigenen Adapter, und der Datenbankadapter setzt die Aufrufe der Speicherschnittstelle in Befehle für die Datenbank um. Diese zusätzlichen Programmteile bedeuten Aufwand beim Entwickeln und Pflegen. Für einen Vorgang ohne fachliche Entscheidung wie „eine feste Liste anzeigen“ bringt eine eigene Speicherschnittstelle oft keinen erkennbaren Nutzen, und viele solche Schnittstellen machen das Programm größer und schwerer zu überblicken. Das Entwicklungsteam muss außerdem dafür sorgen, dass beim Programmstart der Anwendungsfall mit dem passenden Datenbankadapter verbunden wird, im Test mit dem Ersatz im Arbeitsspeicher. Neue Teammitglieder müssen lernen, welcher Code in den Anwendungsfall, welcher in einen Adapter und welcher in die Fachregeln gehört. Damit die Trennung über Jahre erhalten bleibt, braucht es automatische Prüfungen, die unerlaubte Zugriffe melden. Eine solche Prüfung meldet zum Beispiel, wenn der Anwendungsfall „Schaden freigeben“ Code des Datenbankadapters aufruft.
Typische Fehler und woran man sie erkennt:
- Durchgriff auf die Datenbank: Der Anwendungsfall „Schaden freigeben“ greift doch direkt auf Datenbanktabellen zu oder verwendet Einzelheiten der Oberfläche des Schadenportals. Dann ist die Trennung verloren, und der Anwendungsfall muss bei jeder Änderung an Datenbank oder Oberfläche mitgeändert werden. Erkennbar ist das an automatischen Prüfungen, die unerlaubte Zugriffe aus dem Anwendungsfall auf Datenbank- oder Oberflächencode melden, oder an Tests der Regel, die plötzlich eine Datenbank brauchen.
- Regel im Adapter: Ein Entwickler prüft die Grenze von 10.000 Euro zusätzlich im App-Adapter. Liegt ein Schaden über dieser Grenze, meldet der App-Adapter selbst „Eine zweite Freigabe ist erforderlich“ und ruft den Anwendungsfall nicht auf. Später hebt die Versicherung die Grenze auf 20.000 Euro an, und ein Entwickler passt nur den Anwendungsfall an. Für einen Schaden über 15.000 Euro verlangt die App dann weiter eine zweite Freigabe. Im Schadenportal gibt der Anwendungsfall denselben Schaden ohne zweite Freigabe frei. Erkennbar ist das daran, dass Schadenportal und App für denselben Schaden verschiedene Ergebnisse liefern.
- Zugang am Anwendungsfall vorbei: Ein automatischer Import schreibt Freigaben unmittelbar in die Datenbank und umgeht so den Anwendungsfall. Schäden über 10.000 Euro gelten dann ohne zweite Freigabe als freigegeben. Erkennbar ist das an freigegebenen Fällen über der Grenze, zu denen keine zweite Person vermerkt ist.
- Datenbank in der Schnittstelle: Die Speicherschnittstelle bietet statt „Fall laden“ und „Fall speichern“ Vorgänge an, die Tabellennamen oder Datenbankbefehle enthalten. Der Anwendungsfall hängt dann über die Speicherschnittstelle doch von der Datenbank ab. Erkennbar ist das daran, dass ein Datenbankwechsel Änderungen an der Speicherschnittstelle und am Anwendungsfall verlangt.
Weiterführend
Verwandte Ansätze
- Onion Architecture: verwandte Bauweise, die Fachregeln ebenfalls von Oberflächen und Datenbanken trennt.
- Dependency Injection: verbindet beim Programmstart den Anwendungsfall mit dem passenden Datenbankadapter oder im Test mit dem Ersatz im Arbeitsspeicher.
- Repository: ein verbreiteter Name für die Speicherschnittstelle, über die Schadenfälle geladen und gespeichert werden.
Werkzeuge und Umsetzungen
Clean Architecture ist eine Bauweise, kein Produkt. Diese Werkzeuge prüfen automatisch, ob die Bauweise eingehalten wird:
- ArchUnit: prüft in automatischen Tests von Java-Programmen, ob Fachregeln und Anwendungsfälle unerlaubt auf Oberflächen- oder Datenbankcode zugreifen.
- jMolecules: kennzeichnet in Java fachliche und technische Programmteile, sodass Prüfwerkzeuge unerwünschte Abhängigkeiten erkennen.
- dependency cruiser: prüft Abhängigkeiten zwischen Programmteilen in JavaScript- und TypeScript-Projekten.
- Import Linter: prüft, welche Programmteile in Python einander verwenden dürfen.
Quellen
- Robert C. Martin: The Clean Architecture: Aufsatz von 2012, der die Bauweise mit ineinanderliegenden Kreisen und dem Grundsatz einführt, dass Abhängigkeiten nur nach innen zur Fachlogik zeigen dürfen.
- Robert C. Martin: Clean Architecture. A Craftsman's Guide to Software Structure and Design: Buch von 2017, das die Bauweise ausführlich mit Anwendungsfällen, Schnittstellen und Grenzen zwischen Programmteilen erklärt.