© Excellent backgrounds/Shutterstock.com
Architekturpatterns in Modulithen – Teil 2

Wer mit wem reden darf


So manche Codebase macht nur auf den ersten Blick einen aufgeräumten Eindruck. Schön in Packages sortierter Code ohne Abhängigkeitsmanagement ist wie ein „aufgeräumtes“ Kinderzimmer, bei dem einem die Lawine entgegenkommt, wenn man es wagt, die Schranktür aufzumachen. Um zu verhindern, dass Abhängigkeitszyklen und wuchernde Queraufrufe den Code zu einem „Big Ball of Mud“ machen, gilt es, höllisch aufzupassen.

Module in Softwaremonolithen sind toll, das haben wir im ersten Teil dieser Artikelserie gelernt. Das Schneiden von Code in sinnvolle fachliche Kontexte, aus denen man Module bildet, ist dabei schon die halbe Miete zur erfolgreichen Umsetzung dieser Architekturform – aber eben nur die halbe. Um überhaupt in den Genuss ihrer Vorzüge zu kommen, ist es ebenso wichtig, die Interaktionen und Abhängigkeiten der Module sorgfältig zu modellieren.

Schnittstellendesign im Modulithen

Das fängt schon beim Design der Modulschnittstellen an. Welche Operationen soll ein Modul nach außen bereitstellen und in welcher Form gibt es den anderen Modulen seine Daten preis? Ebenso essenziell: Welches Modul darf überhaupt von wem verwendet werden und wie vermeidet man Abhängigkeitszyklen, die die Eigenständigkeit der Module wieder kaputtmachen?

Verliert man diese Fragen aus den Augen, entsteht schnell Wildwuchs im Monolithen. Alles ist auf einmal von jedem abhängig und die Zuständigkeiten zwischen Modulen verschwimmen, womit plötzlich alle Vorteile der modulithischen Architekturform dahin sind. Wartbarkeit und Erweiterbarkeit gehen den Bach runter, unerwartete Nebeneffekte häufen sich. Und das, obwohl man doch so schön geschnittene Module hat. Damit das nicht passiert, gibt es einige Patterns zu befolgen, Antipatterns zu vermeiden und diverse Tools, die einen dabei unterstützen. In diesem Artikel geht es darum, wie man sein Abhängigkeitsmanagement im Modulithen nachhaltig umsetzt. Wir werden uns dabei wieder am Beispiel der Domain „Kino“ entlanghangeln.

Welche Methoden gehören zur Schnittstelle?

Ein wichtiges Konzept in modularer Architektur ist, dass ein Modul die Hoheit über seinen fachlichen Subkontext hat und als einziges bestimmen darf, wie man zugehörige Daten manipuliert. Um das zu ermöglichen, muss das Modul seine interne Logik und Daten verstecken und darf nur Methoden veröffentlichen, die den Clients erlaubte Operationen bereitstellen und die interne Logik in sich kapseln. Wie im ersten Teil der Serie behandelt, ist die einfachste Möglichkeit, das in Java zu bewerkstelligen, das bewusste Setzen der Access Modifier von Methoden – package private für Interna und public für die Schnittstelle.

Hat man sein Modul intern in Subpackages unterteilt, leidet die Kapselung wieder, da die Subpackages modulinterne Methoden als public deklarieren müssen, um sich gegenseitig verwenden zu können. Man hat dann immer noch die Möglichkeit, öffentliche Schnittstellen durch Konventionen zu definieren. Ein geeignetes Mittel dafür ist das klassische Facade-Pattern [1]. Man erstellt in jedem Modul ein Java-Interface, das alle Schnittstellenmethoden definiert. Die Clientkonvention (die jedes Teammitglied kennen muss) ist dann, dass Zugriffe auf ein Modul nur über dessen Interface erlaubt sind.

Ein häufig anzutreffendes Antipattern nennt sich „Visibility for Testing“, das Setzen von privaten Methoden auf public, nur um sie testen zu können. Das ist ein Schuss ins Knie, da man sich die schöne Kapselung gleich wieder kaputt macht. Das Bedürfnis, das zu tun, ist oft ein Hinweis auf schlecht gewählte Klassengröße bzw. Methodenabstraktion. Entweder eine private Methode ist Teil einer zu testenden Unit, die bereits eine public oder package private Methode hat, oder sie gehört zu einer tieferen Abstraktionsebene, die man als eigene, testbare Klasse extrahieren sollte.

Noch gefährlicher wird es, wenn man die Kapselung der Module mit dem Shared-Database-Antipattern umgeht. Es ist in Ordnung, dass alle Module eines Modulithen in dieselbe Datenbank schreiben, solange jedes Modul die Hoheit über seinen eigenen Bereich der Daten hat. Sobald aber zwei Module dieselbe Tabelle beschreiben, besteht wieder die Gefahr von auseinanderlaufender Logik und Konflikten, weshalb der Missbrauch der Datenbank als Schnittstelle dringend zu vermeiden ist.

Wie sehen die Methoden der Schnittstelle aus?

Beim Methodendesign der öffentlichen Schnittstelle eines Moduls ist es selten sinnvoll, einfach nur CRUD-Methoden [2] bereitzustellen. Damit reicht man im Grunde nur die Operationen der Persistenzschicht weiter und gibt den Clients wieder die volle Macht über die Daten. Besser ist es, Business Operation Methods nach den tatsächlichen Operationen zu designen, die auf dem fachlichen Subkontext des Moduls erlaubt sind (Listings 1 und 2).

Listing 1: Schnittstelle mit CRUD-Methoden

// Implementierung der Serviceschnittstelle im Reservierungsmodul public Reservierung read(Long id) { return repository.findById(id); } public void update(Reservierung reservierung) { repository.save(reservierung); } public void delete(Long id) { repository.delete(id); } // Verwendung der Schnittstelle in Clientmodul 1: // Reservierung abholen Reservierung reservierung = reservierungService.read(reservierungId); reservierung.setAbgeholt(true); reservierungService.update(reservierung); // Verwendung der Schnittstelle in Clientmodul 2: // Reservierung abholen reservierungService.delete(reservierungId);

Listing 2: Schnittstelle mit Business Operation Method

// Implementierung der Operation "abholen" im Reservierungsmodul private void abholen (Long reservierungId) { Reservierung reservierung = repository.findById(reservierungId); reservierung.setAbgeholt(true); Reservierung reservierung = repository.save(reservierung); eventPublisher.publishEvent(new ReservierungAbgeholtEvent(reservierung)); } // Verwendung der Schnittstelle in Clientmodul 1: reservierungService.abholen(reservierungId); // Verwendung der Schnittstelle in Clientmodul 2: reservierungService.abholen(reservierungId);

Man sieht, dass im ersten Fall die Clients volle Macht über die Reservierungsdaten haben und jeder damit seine eigene Version der fachlichen Logik implementiert, was dann zu Inkonsistenzen führt. Im zweiten Fall ist die Logik da...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang