© Alexmalexra/Shutterstock.com
Architekturpatterns in Modulithen - Teil 1

Ordnung ins Chaos bringen


„Wir haben diesen Legacy-Monolithen, den wollen wir in Microservices aufbrechen“. So einen Satz hört man als Berater in der Softwarebranche oft. Auf die Frage „Warum“ erhält man oft die Antwort „Modularisierung“. Denn es herrscht die weitverbreitete Ansicht, dass Monolithen grundsätzlich aus schlecht strukturiertem Legacy-Code bestehen und sich Monolithen und Modularisierung gegenseitig ausschließen. Dass dem nicht so ist, zeigt die Architekturform der Modulithen. In dieser Artikelserie wird sie beleuchtet und beschrieben, mit welchen Patterns ein Modulith gelingen kann und welche Antipatterns man dabei vermeiden sollte.

Wer in den letzten sieben Jahren Softwarekonferenzen besucht hat, der musste ganz schön Slalom laufen, wenn er dem Thema Microservices [1] aus dem Weg gehen wollte. Aufgrund ihrer vielversprechenden Eigenschaften wurde diese Form der Systemarchitektur stark gehypt und von vielen Entwicklern, Architekten und Firmen als Heilsbringer angesehen. Mit etwas Abstand betrachtet, handelt es sich dabei aber um keine Silver Bullet, sondern nur um eine von vielen Möglichkeiten, ein Softwaresystem zu strukturieren – mit eigenen Vor- und Nachteilen (Abb. 1). Eine Landschaft aus wirklich entkoppelten Microservices zu bauen, die alle Vorteile dieser Architekturform mitnimmt, ist alles andere als trivial, und man ist selbst dann nicht gegen strukturelle Stolperfallen wie Conway’s Law [2] gefeit. Wenn man Microservices ungünstig schneidet und verknüpft, dann besteht genauso die Gefahr, am Ende einen großen Deployment-Monolithen aus Legacy Code zu erhalten.

franke_modulith_1_1.tif_fmt1.jpgAbb. 1: Vier Jahre später …

Den Hype überlebt haben die „guten alten“ Monolithen. Große Deployment-Einheiten mit einer riesigen Codebase, die sich den Ruf von schlechter Wartbarkeit und Erweiterbarkeit eingehandelt haben. Einerseits überlebten sie, weil man einige davon aufgrund gewachsener Komplexität und schlecht auflösbarer Abhängigkeiten nur schwer wieder losbekommt. Andererseits, weil sie in Form der modularen Monolithen – Modulithen – eine Renaissance in der Entwicklercommunity erleben. Modulithen vereinigen einige der Vorteile der Microservices-Architektur, zum Beispiel modulare Struktur mit gekapselten Verantwortlichkeiten und übersichtlichen Abhängigkeiten, während sie auf einige der Nachteile, wie beispielsweise komplexe Infrastruktur und Kommunikations-Overhead, verzichten. Das macht die Modulithen nicht zu einer den Microservices überlegenen Architekturform (Kasten: „Was können Microservices, was ein Modulith auch kann?“). Sie sind nur ein weiterer valider Ansatz zur Strukturierung eines Softwaresystems, der in bestimmten Kontexten sinnvoller ist als andere.

Die Wahl der Architekturform hängt letztendlich davon ab, auf welchen Aspekt man optimieren möchte. Individuelle Skalierung? Entwicklungsgeschwindigkeit? Resilience? Komplexität der Infrastruktur? Das sind alles Faktoren, die bei dieser Entscheidung eine Rolle spielen.

Dabei ist die Idee des modularen Monolithen durchaus keine neue. Sie ist aber nur erfolgreich und nachhaltig umsetzbar, wenn man es schafft, durch nachhaltige Anwendungsarchitektur die Komplexität einer großen Codebase im Zaum zu halten und Verständlichkeit und Wartbarkeit des Codes zu wahren. Damit habe ich mich in den letzten sieben Jahren in mehreren Softwareprojekten beschäftigt und möchte in dieser Serie einige Patterns und Antipatterns teilen, um anderen Modulithen-Bauern leichter zum Erfolg zu verhelfen.

Was können Microservices, was ein Modulith auch kann?

  • Technische Trennung von fachlichen Kontexten

  • Kleine, weitgehend unabhängig voneinander entwickelbare Module

  • Beherrschbare Komplexität, einfache Erweiterbarkeit

  • Klare Abhängigkeiten mit expliziten Schnittstellen, geringes Risiko für unerwartete Nebeneffekte

  • Continuous Integration, Continuous Delivery

Den Monolithen schneiden

Egal ob man einen historisch gewachsenen Monolithen besser strukturieren will oder ein großes Projekt auf der grünen Wiese startet – eine der ersten Entscheidungen, die man treffen muss, ist: „Wie schneide ich meinen Code in Module?“ Dazu muss man sich bewusst machen, was ein gutes Modul ausmacht: Einer der wichtigsten Aspekte einer modularen Architektur ist die Separation of Concerns. Ein Modul hat eine klare Verantwortlichkeit und ist nur für diese zuständig. Es kümmert sich dabei nicht um Verantwortlichkeiten anderer Module (kein Featureneid [3]). Es kapselt seine Verantwortlichkeit und alle dazu benötigten internen Aspekte wie die Implementierung von Businesslogik und die Persistenz von Daten, sodass sie nicht von außen manipulierbar sind.

Um mit der Außenwelt und anderen Modulen zu kommunizieren, stellt ein Modul Schnittstellen für die notwendigen Operationen bereit, die kontrollierten Zugriff auf seine Verantwortlichkeit erlauben. Es ist möglichst entkoppelt von anderen Modulen und hat so wenige Abhängigkeiten wie möglich. Intern weist es eine hohe Kohäsion auf, d. h., dass es keine „Inseln“ enthält, die mit dem Rest des Moduls nichts zu tun haben.

Dieses Paradigma der geringen Kopplung und hohen Kohäsion schließt es bereits aus, Module nach rein technischen Schichten horizontal zu schneiden. Stellen wir uns z. B. ein Modul vor, das als einzige Verantwortlichkeit Persistenz hat. Darin wären Interfaces/Klassen, die jeweils auf „ihre“ Tabelle in der Datenbank zugreifen. Diese Klassen würden sich nie gegenseitig aufrufen, sondern jeweils nur Datenbankzugriffe vornehmen – also geringe Kohäsion innerhalb des Moduls. Die Kopplung mit „Modulen“ anderer Schichten dagegen wäre vermutlich beträchtlich, da jede datenverarbeitende Logik der Anwendung auf dieses eine Persistenzmodul zugreifen müsste. Damit hätten wir gleichzeitig ein weiteres Antipattern geschaffen, den Dependency Magnet – ein Modul, das Abhängigkeiten zu allen anderen Teilen der Anwendung hat. Wenn man seine ganze Anwendung mit solchen horizontalen Schnitten strukturiert, dann hat man am Ende eine reine Schichtenarchitektur ohne Module und mit einem unübersichtlichen und immer g...

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