Softwarearchitektur verstehen und entscheiden

Aaron KreisÜber mich
Grundlagen

Separation of Concerns

Zuständigkeiten sauber trennen

Die Idee und ihr Nutzen

Im Schadenportal einer Versicherung melden Kunden einen Schaden über ein Formular. Das Schadenportal erledigt dabei drei Tätigkeiten: das Formular anzeigen, die eingegebenen Angaben nach den Regeln der Versicherung prüfen und eine gültige Meldung in der Datenbank speichern. Steckt all das im selben Programmteil, kann schon das Verschieben eines Eingabefeldes versehentlich eine Prüfregel verändern. Deshalb bekommt jede Tätigkeit einen eigenen Programmteil: einen Anzeigebaustein, einen Prüfbaustein und einen Speicherbaustein. Die Grenze zwischen den Programmteilen richtet sich danach, aus welchem Grund ein Programmteil später geändert wird. Ein neues Aussehen des Formulars ändert zum Beispiel nur den Anzeigebaustein, eine neue Prüfregel der Versicherung nur den Prüfbaustein. Dieser Grundsatz heißt Separation of Concerns, auf Deutsch Trennung der Zuständigkeiten. Eine Zuständigkeit, englisch Concern, ist hier eine der drei Tätigkeiten, etwa das Prüfen der Angaben.

Bausteine: Anzeigebaustein: ändert sich, wenn das Formular anders aussehen soll; Prüfbaustein: ändert sich, wenn sich Prüfregeln ändern; Speicherbaustein: ändert sich, wenn sich die Datenbank ändert; Datenbank: speichert Meldungen. Verbindungen: Anzeigebaustein — Prüfbaustein; Prüfbaustein — Speicherbaustein; Speicherbaustein — Datenbank. Anzeigebaustein, Prüfbaustein und Speicherbaustein sind getrennte Programmteile; jeder wird nur aus seinem eigenen Grund geändert.

Wie funktioniert es?

Das Designteam ist für das Aussehen des Schadenportals zuständig. Im Frühjahr ordnet das Designteam die Felder des Formulars neu an und überarbeitet den Hilfetext zum Schadendatum. Dafür ändert das Designteam nur den Anzeigebaustein. Im Sommer verlangt die Fachabteilung, die die Regeln für Schadenmeldungen festlegt, eine neue Regel: Liegt das Schadendatum vor dem Vertragsbeginn, wird die Meldung angenommen, aber zur manuellen Prüfung markiert. Das Entwicklungsteam des Schadenportals ergänzt diese Regel im Prüfbaustein.

Danach meldet ein Kunde einen Schaden und trägt im Formular Vertragsnummer, Schadendatum und eine Beschreibung ein. Der Anzeigebaustein zeigt das Formular an, nimmt die Eingaben entgegen und übergibt sie dem Prüfbaustein, ohne selbst Regeln zu prüfen. Der Prüfbaustein wendet alle Prüfregeln an, auch die neue Regel zum Schadendatum. Gültige Meldungen übergibt der Prüfbaustein dem Speicherbaustein, markierte Meldungen zusammen mit dem Vermerk „manuelle Prüfung“. Der Speicherbaustein schreibt die Meldung in die Datenbank. Versicherungsmakler nutzen einen zweiten Zugang: Ihre eigenen Programme schicken Schadenmeldungen über die Maklerschnittstelle direkt an das Schadenportal. Die Maklerschnittstelle ruft denselben Prüfbaustein auf wie der Anzeigebaustein, und der Prüfbaustein übergibt die gültige Maklermeldung ebenfalls dem Speicherbaustein. Die neue Regel gilt damit sofort für Formular und Maklerschnittstelle, ohne dass das Entwicklungsteam die Regel zweimal schreibt.

Jede der drei Zuständigkeiten ändert sich aus einem eigenen Grund. Den Anzeigebaustein ändert das Designteam, wenn das Formular anders aussehen soll. Im Prüfbaustein setzt das Entwicklungsteam neue Vorgaben der Fachabteilung um. Den Speicherbaustein ändert das Entwicklungsteam, wenn es die Datenbank umbaut; der Speicherbaustein ist der einzige Programmteil, der auf die Datenbank zugreift. Weil die drei Gründe unabhängig voneinander sind, sollen die drei Bausteine so getrennt sein, dass eine Änderung an einem Baustein die anderen beiden nicht berührt. Die Aufrufe gehen nur in eine Richtung: Jeder Baustein ruft nur den Baustein auf, der in der Reihenfolge Anzeige, Prüfung, Speicherung nach ihm kommt. Jede Prüfregel steht nur im Prüfbaustein, und auch jeder weitere Zugang für Schadenmeldungen ruft den Prüfbaustein auf. Der Prüfbaustein läuft auf dem Server des Schadenportals und nicht im Browser des Kunden, weil sich eine Prüfung im Browser umgehen lässt. Die drei Bausteine können innerhalb desselben Programms liegen, und nicht jede einzelne Prüfregel braucht einen eigenen Baustein oder eine eigene Datei.

Lehnt der Prüfbaustein eine Meldung ab, übergibt er sie nicht dem Speicherbaustein, und in der Datenbank entsteht kein Eintrag.

Trennung der Zuständigkeiten: Anzeige, Prüfung und Speicherung getrennt

Beteiligte: Kunde: meldet einen Schaden im Portal; Anzeigebaustein: zeigt das Formular, nimmt Eingaben an; Maklerschnittstelle: zweite Eingangsstelle; Prüfbaustein: alle Prüfregeln, auf dem Server; Speicherbaustein: einziger Zugriff auf die Datenbank; Datenbank: speichert Meldungen. Ablauf: 1. Der Kunde füllt das Formular aus. Eine Änderung am Design des Formulars betrifft nur den Anzeigebaustein. 2. Der Anzeigebaustein übergibt die Eingaben dem Prüfbaustein und prüft selbst keine Regeln. 3. Der Prüfbaustein wendet die neue Regel der Fachabteilung aus dem Sommer an: Liegt das Schadendatum vor dem Vertragsbeginn, nimmt der Prüfbaustein die Meldung an und markiert sie zur manuellen Prüfung. 4. Der Prüfbaustein übergibt gültige Meldungen dem Speicherbaustein, markierte zusammen mit dem Vermerk „manuelle Prüfung“. 5. Der Speicherbaustein schreibt die Meldung in die Datenbank. Kein anderer Baustein greift auf die Datenbank zu. 6. Für die Meldung eines Versicherungsmaklers ruft die Maklerschnittstelle denselben Prüfbaustein auf wie der Anzeigebaustein. 7. Der Prüfbaustein übergibt die gültige Maklermeldung dem Speicherbaustein. Die neue Regel gilt so für Formular und Maklerschnittstelle, ohne dass das Entwicklungsteam die Regel zweimal schreibt.

Wann ist es sinnvoll und wann nicht?

Separation of Concerns hilft

  • Teile eines Programms werden aus unterschiedlichen Gründen und von unterschiedlichen Personen geändert. Im Schadenportal ändert das Designteam das Formular, und das Entwicklungsteam setzt neue Prüfregeln der Fachabteilung um. Ohne getrennte Bausteine arbeiten beide Teams im selben Programmteil, und nach jeder Änderung am Formular muss das Entwicklungsteam prüfen, ob die Prüfregeln noch stimmen.
  • Dieselbe fachliche Regel wird an mehreren Zugängen gebraucht. Stecken die Prüfregeln im Code des Formulars, muss das Entwicklungsteam sie für die Maklerschnittstelle kopieren. Die beiden Kopien weichen mit der Zeit voneinander ab. Mit einem eigenen Prüfbaustein rufen Anzeigebaustein und Maklerschnittstelle dieselben Prüfregeln auf.
  • Teile sollen einzeln getestet werden. Der Prüfbaustein lässt sich ohne Formular und Datenbank testen. Steckt alles im selben Programmteil, braucht jeder Test der Prüfregeln auch Formular und Datenbank.

Separation of Concerns hilft nicht

  • Das Programm ist sehr klein und ändert sich kaum, etwa ein Programm, mit dem Kunden nur ihre Telefonnummer ändern. Eigene Bausteine würden dann Änderungen auseinanderhalten, die kaum vorkommen, und kein zweiter Zugang braucht dieselbe Prüfung. Dann schreibt das Entwicklungsteam Anzeige, Prüfung und Speicherung nur in getrennte Funktionen, also einzeln aufrufbare Abschnitte des Codes. Eigene Bausteine mit einer festgelegten Form für jede Übergabe muss das Entwicklungsteam dann nicht entwerfen und bei Änderungen nicht mit anpassen.
  • Mehrere Teile des Codes ändern sich immer gemeinsam und aus demselben Grund. Ein Beispiel sind die Regeln zum Schadendatum, die die Fachabteilung immer gemeinsam ändert. Dann lässt das Entwicklungsteam diese Regeln zusammen im Prüfbaustein. Jede Änderung betrifft so nur diesen einen Baustein. Bei getrennten Bausteinen müsste das Entwicklungsteam bei jeder Änderung mehrere Bausteine und die Übergaben zwischen ihnen anpassen.

Nachteile und typische Fehler

Jede Übergabe zwischen zwei Bausteinen braucht eine festgelegte Form. Die Form bestimmt zum Beispiel, welche Angaben der Anzeigebaustein dem Prüfbaustein übergibt. Ändert sich diese Form, müssen beide Bausteine angepasst werden. Außerdem bleibt die Trennung nur erhalten, wenn das Entwicklungsteam bei jeder Änderung darauf achtet, in welchen Baustein die Änderung gehört.

Typische Fehler und woran man sie erkennt:

  • Prüfregel im Anzeigebaustein: Eine Prüfregel landet im Anzeigebaustein statt im Prüfbaustein. Die Maklerschnittstelle ruft den Prüfbaustein direkt auf, ohne den Anzeigebaustein. Meldungen über die Maklerschnittstelle durchlaufen diese Regel deshalb nicht. Erkennbar ist das an Maklermeldungen, die bei der Eingabe über das Formular abgelehnt worden wären.
  • Prüfung nur im Browser: Das Entwicklungsteam baut die Regel zum Schadendatum nur in das Formular im Browser des Kunden ein und nicht in den Prüfbaustein. Ein Kunde umgeht die Prüfung im Browser und schickt eine Meldung mit einem Schadendatum vor dem Vertragsbeginn. Der Prüfbaustein markiert die Meldung nicht, und der Speicherbaustein schreibt sie ohne den Vermerk „manuelle Prüfung“ in die Datenbank. Erkennbar ist das an Meldungen mit einem Schadendatum vor dem Vertragsbeginn, die ohne diesen Vermerk in der Datenbank stehen.
  • Falsch gezogene Grenzen: Zusammengehörende Regeln sind auf mehrere Bausteine verteilt. Das Entwicklungsteam legt zum Beispiel für jede Regel zum Schadendatum einen eigenen Baustein an, obwohl die Fachabteilung diese Regeln immer gemeinsam ändert. Bei jeder Änderung muss das Entwicklungsteam trotz der Trennung mehrere Bausteine anfassen. Erkennbar ist das an Änderungen, die regelmäßig mehrere Bausteine gleichzeitig betreffen.
  • Bausteine ohne eigene Aufgabe: Das Entwicklungsteam fügt zwischen Anzeigebaustein, Prüfbaustein und Speicherbaustein weitere Bausteine ein, die einen Aufruf nur an den nächsten Baustein weiterreichen. Wer eine Prüfregel ändern will, muss sich durch viele Dateien arbeiten. Erkennbar ist das an Bausteinen, die selbst nichts anzeigen, prüfen oder speichern.
  • Umgangener Speicherbaustein: Der Prüfbaustein schreibt markierte Meldungen selbst in die Datenbank, statt sie dem Speicherbaustein zu übergeben. Baut das Entwicklungsteam die Datenbank um, muss es dann neben dem Speicherbaustein auch den Prüfbaustein ändern. Erkennbar ist das an Datenbankzugriffen außerhalb des Speicherbausteins.

Weiterführend

Verwandte Ansätze

  • Model View Controller: wendet den Grundsatz auf Bedienoberflächen an. Eine Oberfläche wird dabei auf drei Rollen verteilt: eine für Daten und Regeln, eine für die Anzeige und eine für die Steuerung.
  • Layered Architecture: ordnet die getrennten Zuständigkeiten bei größeren Programmen in feste, übereinanderliegende Ebenen wie Oberfläche, fachliche Regeln und Datenzugriff, die nur in einer Richtung aufeinander zugreifen.
  • Loose Coupling and High Cohesion: beschreibt das Ziel hinter der Trennung: Was zusammen geändert wird, liegt beieinander, und getrennte Teile hängen möglichst wenig voneinander ab.
  • Clean Architecture: treibt die Trennung weiter, sodass die fachlichen Regeln weder von der Oberfläche noch von der Datenbank abhängen.

Quellen

  • Martin Fowler: PresentationDomainDataLayering: beschreibt die Trennung von Anzeige, fachlichen Regeln und Datenzugriff, zeigt, dass sie innerhalb eines Programms gelingt, und nennt als Gründe, dass Entwickler sich jeweils auf einen Teil konzentrieren können, dass mehrere Oberflächen dieselben fachlichen Regeln nutzen und dass sich diese Regeln leichter testen lassen.
  • Robert C. Martin: The Single Responsibility Principle: begründet, dass Programmteile danach getrennt werden sollten, aus welchem Grund und auf wessen Wunsch sie sich ändern.