Zwei Java Frameworks für Modularität (Angewandte Modularität, Teil 5)

In dem letzten Blog zur Serie „Angewandte Modularität“ hatte ich die Fachdomäne HOO (kurz für HOroscope Online) vorgestellt, an deren Beispiel ich verschiedene Modularitätsansätze vergleichen möchte. Der letzte Blog stellte auch eine erste HOO-Implementierung vor, nämlich die des klassischen Monolithen. In diesem Blog möchte ich drei weitere HOO-Implementierungen vorstellen, die des verteilten Monolithen, eine OSGi-Implementierung und eine Java9Modules-Variante.

Die HOO-Fachdomäne (wie sie im Flussdiagramm des letzten Blogs modelliert wurde) legt die Bildung von folgenden vier Komponenten nahe: Control, Order, Horoscope und Invoice. Der klassische Monolith lässt sich relativ einfach in einen verteilten Monolithen umbauen, indem die bestehende DAO- und Domain-Klassen in die Projekte ihrer jeweiligen Komponenten verschoben werden. Die Projekte der vier Komponenten müssen sich gegenseitig kennen, um notwendige Klassen zu referenzieren. Die Abhängigkeiten werden als Maven-Dependencies in den POM files definiert und dabei müssen zyklische und wechselseitige Abhängigkeiten vermieden werden, weil Maven diese nicht auflösen kann. Wie das konkret aussehen kann, ist unter  https://github.com/iks-github/DemoCode/wiki/HOO-Pseudo-Modular-Monolith beschrieben. Analysiert man die Abhängigkeiten dieser Implementierung ergibt sich folgendes Bild:

Die Control-Komponente mit der Hauptklasse HOroscopeOnlineService.java muss alle drei übrigen Komponenten kennen (eine sogenannte High-Level-Komponente), weil für den Ablauf der Geschäftslogik bestimmte Methoden aufgerufen werden müssen. Außerdem braucht die Komponente Invoice Abhängigkeiten auf die Komponenten Horoscope und Order, weil deren Datenobjekte verwendet werden. Aus dem gleichen Grund muss Horoscope auch Order kennen. Nur die Komponente Order kommt ohne Abhängigkeiten aus (eine sogenannte Low-Level-Komponente).

Wie im vorletzten Blog (Teil 3) beschrieben, ist diese Art der Modularisierung nur pseudo-modular, weil es keinen Modulvertrag gibt, der einen öffentlich zugänglichen Teil der Sourcen von  privaten Modulinnereien trennt. Daher können beliebige Abhängigkeiten zwischen den beliebigen Klassen von beliebigen Modulen bestehen, was zu einer sehr unmodularen Verzahnung der Komponenten führt: also zu einem verteilten Monolithen (siehe erste Abbildung in Teil 3). Die Kopplung der Komponenten ist damit so stark, dass eine Erweiterbarkeit der Anwendung stark eingeschränkt ist.

Echte Modularisierung bietet das Java-Modularisierungsframework OSGi. Dabei handelt es sich um eine Spezifikation, wie Modularität sauber umzusetzen ist. Eine bekannte Implementierung der OSGi-Spezifikation ist Equinox, das von Eclipse RCP benutzt wird. Zu einem OSGi-Modul (bundle genannt) gehört eine sogenannte Manifest-Datei, die bestimmte Java-Packages als Export-Packages deklariert und angibt, welche andere Module (sogenannte Required Bundles) oder fremde Java-Packages (sogenannte Import-Packages) von diesem Bundle benötigt werden. OSGi-Anwendungen werden gestartet, indem der sogenannte OSGi-Container gestartet wird. Dieser sucht nach zur Verfügung stehenden Bundles, versucht sie zu laden und wertet den Modul-Vertrag aus, der in der Manifest-Datei deklariert ist. Auf Basis der Modulverträge sämtlicher Bundles versucht der OSGi-Container alle Abhängigkeiten zwischen den Bundles aufzulösen und dann die einzelnen Bundles zu starten.

Wie die HOO-Fachdomäne als OSGi-Anwendung aussieht, lässt sich hier begutachten: https://github.com/iks-github/DemoCode/tree/master/HOroscopeOnlineService/orchestration/hoo-osgi-loose-coupling. Wie man diesen Code in der IDE zum Laufen bekommt ist hier erklärt: https://github.com/iks-github/DemoCode/wiki/HOO-OSGi. In dieser HOO-Implementierung gehört das Package com.iksgmbh.demo.hoo.order.utils nicht zur Modulschnittstelle des Moduls Order. Damit kann die Klasse DateStringUtil nur innerhalb dieses Moduls referenziert werden. Der OSGi-Container legt nämlich nur die Klassen der Export-Packages auf den Klassen-Pfad der anderen Module. Auf diese Weise entsteht eine Art Privatsphäre für Ressourcen, deren Package im Modulvertrag nicht als Export-Package deklariert wurde.

Außerdem bietet auch die Programmiersprache Java in der Version 9 echte Modularisierung an (Java9Modules). Eingebettet in die Programmiersprache (und hoffentlich bald auch von den IDEs) können Module definiert und deren Sourcen auf den sogenannten modulepath gelegt werden (der zusätzlich oder alternativ zum classpath genutzt werden kann). Ähnlich OSGi können in einer Datei namens module-info.java die Schnittstelle eines Modules beschrieben werden. Wie das für HOO-Fachdomäne als OSGi-Anwendung aussieht, lässt sich hier begutachten: https://github.com/iks-github/DemoCode/tree/master/HOroscopeOnlineService/orchestration/hoo-java9modules-loose-coupling. Wie man diesen Code in der IDE zum Laufen bekommt ist hier erklärt: https://github.com/iks-github/DemoCode/wiki/HOO-Java9Modules.

 

Vergleicht man die drei genannten Formen der Modularisierung (bzw. ihre Implementierungen) bezüglich der Art, wie Abhängigkeiten zwischen den Komponenten bzw. Modulen definiert werden, ergibt sich folgendes Bild:

Dieser Vergleich macht zum einen deutlich, wie ähnlich mit OSGi und Java9 Modulverträge definiert werden und zum anderen, dass Maven nicht wirklich ein Modularitätsframework darstellt, weil der von außen zugreifbare Teil eines Pseudomoduls (d.h. ein vollständiger Modulvertrag) gar nicht definiert werden kann.

Mehr Details zu Java9Modules, OSGi und loser Kopplung zwischen Komponenten finden sich unter https://leanpub.com/applied-modularity. Trotz echter Modularität und entkoppelten Komponenten mit OSGi und Java9Modules, haben die beiden Lösungen ein Problem und zwar eines, das auch der pseudo-modulare Monolith hat: Zyklische und wechselseitige Abhängigkeiten zwischen den Modulen müssen strikt verhindert werden. Im nächsten Blog werde ich zwei HOO-Varianten vorstellen, bei denen das nicht der Fall ist.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.