Layered Architecture
Schichten mit festgelegter Aufrufrichtung
Die Idee und ihr Nutzen
Ein Sachbearbeiter einer Versicherung klickt in der Schadenanwendung bei Schaden 4711 auf „Freigeben“. Innerhalb der Schadenanwendung sind daran vier Programmteile nacheinander beteiligt, und jeder hat genau eine Aufgabe. Die Präsentationsschicht nimmt den Klick entgegen, die Anwendungsschicht legt fest, in welcher Reihenfolge der Schaden geladen, geprüft und gespeichert wird, die Fachschicht prüft die Freigaberegeln, und die Datenzugriffsschicht speichert das Ergebnis in der Datenbank. Die vier Programmteile liegen in dieser Reihenfolge übereinander, und Aufrufe gehen nur von oben nach unten. Die Freigaberegeln stehen damit nur in der Fachschicht. Die Präsentationsschicht lässt sich zum Beispiel ändern, ohne die anderen drei Programmteile anzufassen, solange die Präsentationsschicht die Anwendungsschicht weiter gleich aufruft. Solche übereinanderliegenden Programmteile heißen Schichten, die Bauweise heißt Layered Architecture, auf Deutsch Schichtenarchitektur.
Wie funktioniert es?
Der Sachbearbeiter klickt bei Schaden 4711 auf „Freigeben“. Die Präsentationsschicht nimmt den Klick entgegen und ruft in der Anwendungsschicht freigeben(4711) auf. Die Anwendungsschicht bestimmt die Reihenfolge und lässt den Schaden zuerst über die Datenzugriffsschicht laden. Die Datenzugriffsschicht fragt den Schaden bei der Datenbank ab. Die Datenbank liefert Schaden 4711 mit 6.000 Euro, und die Datenzugriffsschicht gibt den Schaden an die Anwendungsschicht zurück. Dann übergibt die Anwendungsschicht den Schaden und die Angaben zum Sachbearbeiter an die Fachschicht. Die Fachschicht prüft, ob der Sachbearbeiter einen Schaden dieser Höhe freigeben darf. 6.000 Euro liegen unter der Freigabegrenze von 10.000 Euro, und die Fachschicht antwortet „erlaubt“. Die Anwendungsschicht lässt die Datenzugriffsschicht den neuen Stand in einer Datenbanktransaktion speichern. Eine Datenbanktransaktion fasst Änderungen so zusammen, dass die Datenbank sie ganz oder gar nicht übernimmt. Zum Schluss meldet die Anwendungsschicht das Ergebnis an die Präsentationsschicht, und die Präsentationsschicht zeigt „freigegeben“.
Die Präsentationsschicht ist die Oberfläche der Schadenanwendung oder die Schnittstelle, über die andere Programme die Schadenanwendung aufrufen. In manchen Anwendungen fehlt die Anwendungsschicht, und die Präsentationsschicht ruft die Fachschicht direkt auf. Die Fachschicht enthält die Geschäftsregeln. Eine davon ist die Freigabegrenze: Bis 10.000 Euro darf ein Sachbearbeiter Schäden allein freigeben, Schäden darüber brauchen eine zweite Freigabe. Bei hohen Schäden wird ein Fehler teuer. Die Fachabteilung hat die Grenze deshalb so gewählt, dass nur diese wenigen hohen Schäden ein zweites Mal geprüft werden. Nur die Datenzugriffsschicht liest und schreibt Daten in der Datenbank. Eine untere Schicht darf nie eine obere aufrufen, nur die Antworten gehen nach oben. Bei einer strengen Schichtung darf jede Schicht nur die direkt darunterliegende Schicht aufrufen. Bei einer lockeren Schichtung darf eine Schicht auch tiefere Schichten direkt aufrufen. Die Freigabe von Schaden 4711 folgt der lockeren Schichtung: Zum Laden und Speichern ruft die Anwendungsschicht die Datenzugriffsschicht direkt auf und überspringt dabei die Fachschicht. Die Schichtenarchitektur legt nur fest, welche Schicht welche aufrufen darf. Einen unerlaubten Aufruf verhindert die Schichtenarchitektur nicht von selbst.
Bei einem Schaden über 10.000 Euro lehnt die Fachschicht die Freigabe ab. Die Anwendungsschicht lässt dann nichts speichern, und die Präsentationsschicht zeigt den Grund an. Scheitert dagegen das Speichern, weil die Datenbank nicht erreichbar ist, darf die Präsentationsschicht nicht „freigegeben“ anzeigen. Die Freigaberegel ist dann zwar erfüllt, der neue Stand aber nicht gespeichert.
Schichtenarchitektur: eine Freigabe durchläuft vier Schichten
Wann ist es sinnvoll und wann nicht?
Eine Schichtenarchitektur lohnt sich
- Ein Team entwickelt eine Geschäftsanwendung, die als ein Programm zusammen mit ihrer Datenbank betrieben wird. Das Team braucht eine Aufteilung des Codes, die fast jeder Entwickler schon kennt, mit je einem Programmteil für Oberfläche, Ablauf, Geschäftsregeln und Datenzugriff. Eine solche Aufteilung brauchen die meisten Geschäftsanwendungen. Ohne eine bekannte Aufteilung erfindet das Team eine eigene, und jeder neue Entwickler muss sie erst lernen.
- Mehrere Entwickler arbeiten gleichzeitig an der Anwendung, und neue kommen hinzu. Ohne Schichten kann eine Regel überall stehen, und neue Entwickler finden sie nicht. Mit Schichten steht die Freigabegrenze von 10.000 Euro nur in der Fachschicht, und niemand muss dafür jede Bildschirmmaske durchsuchen.
- Ohne Schichten vermischt sich der Code mit der Zeit. Der Code einer Bildschirmmaske enthält dann Datenbankabfragen. Eine Datenbankabfrage enthält eine Geschäftsregel. Eine Geschäftsregel erzeugt selbst eine Meldung auf dem Bildschirm. Ändert ein Entwickler das Aussehen einer Maske, kann er versehentlich die Prüfung der Freigabegrenze verändern, weil die Prüfung im selben Code steht. In der Schichtenarchitektur steht diese Prüfung nicht im Code der Maske.
- Die Schadenanwendung soll neben dem bisherigen Arbeitsplatzprogramm eine Weboberfläche für Sachbearbeiter im Außendienst bekommen. Das Team baut dafür nur eine neue Präsentationsschicht, die dieselbe Anwendungsschicht aufruft. Fachregeln und Datenzugriff bleiben unverändert, weil in der alten Oberfläche keine Regel steht. Ohne Schichten müssten die Regeln erst aus dem Code der alten Masken herausgelöst werden.
- Die Geschäftsregeln ändern sich unabhängig davon, wie die Oberfläche aussieht oder welche Datenbank die Schäden speichert. Ändert sich der Aufbau einer Tabelle, betrifft das meist nur die Datenzugriffsschicht. Ohne Schichten stehen die Datenbankabfragen auch im Code der Masken und der Geschäftsregeln, und eine geänderte Tabelle verlangt Änderungen an all diesem Code.
Eine Schichtenarchitektur ist nicht das Richtige
- Die Fachschicht enthält viele Geschäftsregeln, und ihre Tests sollen ohne Datenbank laufen. In der Schichtenarchitektur darf die Fachschicht aber die Datenzugriffsschicht aufrufen. Muss die Fachschicht für eine Regel zum Beispiel selbst die bisherigen Zahlungen zu Schaden 4711 laden, braucht jeder Test dieser Regel eine laufende Datenbank. Stattdessen baut das Team die Anwendung nach der Hexagonal Architecture, der Onion Architecture oder der Clean Architecture. Bei allen drei Bauweisen legt das Team zusammen mit den Geschäftsregeln fest, welchen Aufruf die Geschäftsregeln zum Laden und Speichern brauchen, und die Datenbankanbindung bietet diesen Aufruf an. Im Test ersetzt das Team die Datenbankanbindung durch einen Ersatz, der die Schäden im Arbeitsspeicher hält, und die Tests der Geschäftsregeln laufen ohne Datenbank.
- Eine große Anwendung soll nach Fachgebieten zerlegt werden. Schichten teilen nach technischer Aufgabe, nicht nach Fachgebieten wie Schaden, Vertrag oder Zahlung. Eine Änderung am Fachgebiet Schaden betrifft dann Code in allen vier Schichten, und in jeder Schicht steht dieser Code neben dem Code für Vertrag und Zahlung. Stattdessen teilt das Team die Anwendung als Modular Monolith zuerst nach Fachgebieten in Module auf. Jedes Modul kann innen weiter in Schichten geordnet sein. Eine Änderung am Fachgebiet Schaden betrifft dann nur das Modul für Schäden.
- Eine Anwendung liest fast nur Daten aus und zeigt sie an. Anwendungsschicht und Fachschicht würden dann nur Aufrufe weiterreichen. Stattdessen lässt das Team den Code der Oberfläche die Datenbank direkt abfragen und spart sich den Code der Schichten, die nur weiterreichen würden.
Nachteile und typische Fehler
Ruft die Fachschicht die Datenzugriffsschicht auf, muss die Fachschicht die Aufrufe der Datenzugriffsschicht kennen. Enthält ein solcher Aufruf zum Beispiel einen Tabellennamen der Datenbank, muss bei einem Wechsel der Datenbank auch die Fachschicht geändert werden. Heißt der Aufruf dagegen fachlich ladeSchaden(4711), bleibt die Fachschicht bei einem Wechsel der Datenbank unverändert. Auch Anfragen, die nur Daten anzeigen, durchlaufen mehrere Schichten, selbst wenn die mittleren Schichten nur weiterreichen. Das ist zusätzlicher Code, der geschrieben und getestet werden muss. Eine fachliche Änderung betrifft oft alle Schichten gleichzeitig. Ein neues Feld muss zum Beispiel von der Maske bis zur Datenbanktabelle ergänzt werden. Damit die Grenzen zwischen den Schichten dauerhaft eingehalten werden, braucht das Team eine automatische Prüfung, die bei jedem Übersetzen und Testen des Codes unerlaubte Aufrufe meldet.
Typische Fehler und woran man sie erkennt:
- Aufruf nach oben: Ein Entwickler lässt die Datenzugriffsschicht beim Speichern noch einmal die Freigaberegel der Fachschicht aufrufen. Die Datenzugriffsschicht hängt dann von der Fachschicht ab, die über ihr liegt. Eine Änderung der Freigaberegel kann so auch das Speichern verändern, und die Datenzugriffsschicht lässt sich nicht mehr ohne die Fachschicht testen. Erkennbar ist das an Meldungen von Prüfwerkzeugen über Aufrufe von unten nach oben oder an Programmteilen, die sich gegenseitig aufrufen.
- Regel in der Oberfläche: Ein Entwickler prüft die Freigabegrenze im Code der Maske statt in der Fachschicht. Die neue Weboberfläche ruft dieselbe Anwendungsschicht auf, aber die Fachschicht prüft die Grenze nicht. Ein Sachbearbeiter im Außendienst kann so über die Weboberfläche einen Schaden über 10.000 Euro allein freigeben. Erkennbar ist das an Fachregeln, die je nach Oberfläche verschieden greifen.
- Durchreichende Schichten: Die Anwendungsschicht hat keine eigene Aufgabe und reicht die meisten Aufrufe eins zu eins an die Fachschicht weiter. Weil niemand festgelegt hat, welche Regeln in die Anwendungsschicht gehören und welche in die Fachschicht, stehen manche Geschäftsregeln in der Anwendungsschicht und andere in der Fachschicht. Erkennbar ist das, wenn Entwickler für eine Regel in beiden Schichten suchen müssen.
- Fachgebiete durcheinander: In einer großen Anwendung liegen Schaden, Vertrag und Zahlung in jeder Schicht vermischt. Wer ein Fachgebiet ändern will, muss es in jeder Schicht zusammensuchen. Erkennbar ist das an Änderungen für ein einziges Fachgebiet, die Dutzende Dateien in allen Schichten berühren.
Weiterführend
Verwandte Ansätze
- Separation of Concerns: der Grundsatz, jede Aufgabe in genau einem Programmteil zu erledigen. Die Schichtenarchitektur setzt ihn für technische Aufgaben um.
- Model View Controller: ordnet die Präsentationsschicht weiter in Daten, Anzeige und Steuerung.
Werkzeuge und Umsetzungen
Diese Werkzeuge prüfen automatisch, ob die Grenzen zwischen den Schichten eingehalten werden:
- ArchUnit: enthält eine fertige Prüfung für Schichten, die unerlaubte Aufrufe in Java-Code findet.
- jQAssistant: untersucht Code und prüft Architekturregeln wie Schichtgrenzen.
- dependency cruiser: prüft Regeln für Abhängigkeiten in JavaScript- und TypeScript-Projekten.
- Import Linter: prüft Schichtregeln in Python-Projekten.
Quellen
- Martin Fowler: Presentation Domain Data Layering: beschreibt die Aufteilung in Präsentation, Fachlogik und Datenzugriff, die Richtung der Abhängigkeiten und rät, große Anwendungen zuerst nach Fachgebieten zu teilen.
- Microsoft: N-tier architecture style: unterscheidet logische Schichten von getrennt betriebenen Rechnern und strenge von lockerer Schichtung, bei der eine Schicht jede darunterliegende aufrufen darf.