Die Transformation eines Monolithen in Microservices stellt eine der größten Herausforderungen in der modernen Softwarearchitektur dar. Diese Aufgabe erfordert nicht nur tiefgreifendes technisches Wissen, sondern auch eine fundierte Strategie, um die komplexen und stark miteinander verflochtenen Teile eines Monolithen in kleine, unabhängige Microservices zu zerlegen, die miteinander interagieren können. Es gibt mehrere Ansätze und Muster, die diese Transformation unterstützen, wobei der Strangler Pattern einer der bekanntesten ist.

Das Strangler Pattern zielt darauf ab, den bestehenden Monolithen schrittweise zu ersetzen, indem neue Funktionen als Microservices eingeführt werden, während gleichzeitig die alten Funktionen nach und nach deaktiviert werden. Der Vorteil dieses Musters liegt in seiner Flexibilität, da es eine Transformation ohne plötzliche, risikoreiche Änderungen ermöglicht. Das Prinzip ist einfach: Anstatt den gesamten Monolithen auf einmal zu ersetzen, werden nach und nach neue Microservices entwickelt, die jeweils eine Funktion des Monolithen übernehmen. Dieser Prozess wird fortgesetzt, bis der gesamte Monolith aufgelöst ist.

Ein weiterer wichtiger Ansatz in der Umwandlung von Monolithen ist die Extract Components Pattern, bei dem bestehende Komponenten des Monolithen extrahiert und als eigenständige Microservices implementiert werden. Dies kann oft die effektivste Methode sein, da man so bestehende Codebasen nutzt und die Funktionalitäten schrittweise abstrahiert. Häufig wird diese Technik in Verbindung mit einem Monolith to Microservice Proxy verwendet, der als Vermittler zwischen dem alten und dem neuen System fungiert. Der Proxy sorgt dafür, dass die Kommunikation zwischen dem Monolithen und den neuen Microservices reibungslos erfolgt, ohne dass der Benutzer die Transformation bemerkt.

Bei der Implementierung von Microservices ist es außerdem wichtig, die richtigen Bounded Contexts zu definieren. Diese Kontexte helfen dabei, die Domäne des Unternehmens sinnvoll in Microservices zu unterteilen. Jeder Microservice sollte eine klare Verantwortung haben, die nur einen spezifischen Aspekt des Geschäftsprozesses abdeckt. Dies führt zu einer besseren Wartbarkeit und Skalierbarkeit, da jede Einheit unabhängig voneinander entwickelt, getestet und skaliert werden kann.

Ein wichtiger Punkt bei der Umstellung auf Microservices ist die Wahl des Abstraktionsniveaus. Die Definition eines geeigneten Abstraktionsniveaus für die Microservices und deren Schnittstellen ist entscheidend für den Erfolg der Transformation. Eine zu hohe Abstraktion kann zu Problemen bei der Integration führen, während eine zu niedrige Abstraktion die Wartbarkeit und Erweiterbarkeit erschwert. Zudem ist die Einführung eines gemeinsamen Ubiquitous Language zwischen den Entwicklern und den Fachexperten von zentraler Bedeutung. Diese gemeinsame Sprache gewährleistet, dass alle Beteiligten die gleiche Vision von den zu entwickelnden Microservices haben und so Missverständnisse und Fehler vermieden werden.

Während der Transformation von Monolithen zu Microservices ist es auch wichtig, sich mit den Herausforderungen der Containerisierung auseinanderzusetzen. Container bieten den Vorteil, dass sie eine isolierte Umgebung schaffen, die es ermöglicht, Microservices unabhängig voneinander zu betreiben. Dies erleichtert nicht nur die Skalierung der Anwendungen, sondern sorgt auch für eine bessere Ressourcennutzung. Containertechnologien wie Docker haben sich dabei als besonders nützlich erwiesen, da sie eine schnelle Bereitstellung und Verwaltung von Microservices ermöglichen.

Ein weiteres häufig angewendetes Prinzip in modernen Cloud-Architekturen ist die Verwendung der Twelve-Factor App Practices. Diese Best Practices umfassen unter anderem eine klare Trennung der Konfiguration vom Code, die Verwendung von stateless Services und das Management von Prozessen über eine konsistente API. Sie bieten eine solide Grundlage für die Entwicklung von Microservices, da sie eine standardisierte Vorgehensweise gewährleisten, die zu einer höheren Effizienz und Skalierbarkeit führt.

Die Entscheidung, den Monolithen in Microservices zu transformieren, sollte stets in Betracht ziehen, dass dieser Prozess mit zahlreichen Herausforderungen verbunden ist. Die Migration von Legacy-Systemen erfordert ein gründliches Verständnis des bestehenden Codes und eine präzise Planung der Übergangsphase. Besonders die Sicherstellung der Konsistenz und die Handhabung von Daten sind kritische Faktoren, die den Erfolg der Transformation beeinflussen können. Es ist auch wichtig zu verstehen, dass Microservices nicht die Lösung für jedes Problem sind. Sie bringen ihre eigenen Herausforderungen mit sich, wie etwa die Komplexität der Kommunikation zwischen verschiedenen Microservices und das Management von verteilten Systemen.

Insgesamt ist die Transformation von Monolithen in Microservices ein komplexer, aber lohnenswerter Prozess. Sie ermöglicht es, Systeme flexibler, skalierbarer und besser wartbar zu machen. Doch dieser Prozess erfordert nicht nur technisches Wissen, sondern auch eine sorgfältige Planung und ein gutes Verständnis der zugrundeliegenden Geschäftsprozesse. Ein fundierter Ansatz bei der Partitionierung der Monolithen, die Wahl der richtigen Patterns und die kontinuierliche Kommunikation zwischen den Entwicklern und den Domain-Experten sind entscheidend für den Erfolg dieses Vorhabens.

Wie lässt sich bestehende Funktionalität in eine Microservices-Architektur integrieren, ohne den Monolith zu verändern?

In vielen Fällen ist es nicht möglich oder nicht praktikabel, bestehende Funktionalität vollständig neu als Microservices zu implementieren. Die Gründe dafür reichen von fehlendem Zugriff auf den Quellcode über Abhängigkeit von extern gehosteten Diensten bis hin zu hohem Risiko bei Änderungen an geschäftskritischen Altsystemen. Dennoch besteht häufig die Notwendigkeit, die Funktionalität solcher Systeme in moderne, cloud-native Architekturen zu integrieren, ohne dabei deren bestehende Implementierung anzutasten.

Ein wirkungsvoller Weg, diese Herausforderung zu lösen, ist der Einsatz sogenannter Adapter-Microservices. Dabei handelt es sich um dedizierte Microservices, die ein bestehendes System of Record (SoR) – also ein externes oder internes Bestandssystem – über eine standardisierte Service-API verfügbar machen. Der Adapter-Microservice agiert als Vermittlungsschicht zwischen dem neuen Microservices-basierten System und dem bestehenden System, dessen Funktionalität erhalten bleiben soll.

Der Adapter übernimmt die Rolle eines Proxys oder Adapters im Sinne klassischer Entwurfsmuster: Er kapselt das Altsystem, gleicht inkompatible Schnittstellen an und ermöglicht eine kontrollierte Kommunikation über Netzwerkgrenzen hinweg. Wie bei der klassischen Adapter Pattern wird die Schnittstelle des SoR in eine neue API transformiert, die den Erwartungen der modernen Architektur entspricht. Für die Clients des neuen Systems sieht es aus, als würden sie mit einem normalen Microservice sprechen – tatsächlich werden jedoch die Funktionen des bestehenden Systems durchgereicht, ggf. transformiert oder angereichert.

Diese Vorgehensweise ähnelt dem Prinzip des Enterprise Service Bus (ESB) aus der Serviceorientierten Architektur (SOA), unterscheidet sich jedoch durch die klarere Entkopplung und die verteilte Prozessarchitektur. Während ein ESB typischerweise zentralisiert ist und oft zu einem Engpass wird, kann ein Adapter-Microservice unabhängig deployt, skaliert und gewartet werden.

Adapter-Microservices enthalten in der Regel keine oder nur minimale Fachlogik. Ihre Hauptaufgabe liegt in der Integrationslogik – sie transformieren Datenformate, übersetzen Protokolle, führen Validierungen durch oder fügen Funktionen wie Caching oder Retry-Mechanismen hinzu, um die Zuverlässigkeit des nicht veränderbaren Systems zu verbessern. Die eigentliche Geschäftslogik bleibt vollständig im SoR erhalten. Soll zusätzliche Fachlogik nötig sein, wird diese in separaten Domain-Microservices abgebildet, die den Adapter als Zugriffsschicht auf das Altsystem nutzen.

Auch wenn das bestehende System bereits über eine brauchbare API verfügt

Wie das Modellieren von Domänen zu einer Schichtenarchitektur führen kann

Innerhalb eines Bounded Context wird eine Anwendungsarchitektur mit verschiedenen Schichten entworfen. Die oberste Schicht stellt die Anwendungsebene dar, die als Interface zum Microservice fungiert. Diese Ebene ist direkt über der Domänenschicht angesiedelt, welche die Aggregate der Entitäten und Value Objects (VOs), die Domain Events und die Domain Services umfasst. In der Praxis interagiert diese Anwendungsschicht häufig mit einer Infrastruktur-Ebene, die das Abrufen von Daten über ein Repository ermöglicht, wobei die Persistenz entweder über relationale (SQL) oder nicht-relationale (NoSQL) Datenbanken erfolgt. Der Zugriff auf den Bounded Context ist ausschließlich über die Anwendungsschicht möglich. Eine zusätzliche Schicht darüber kann eine API- oder GUI-Schicht sein, die zum Beispiel über einen Dispatcher wie ein API Gateway oder ein Backend For Frontend (BFF) fungiert, was gleichzeitig ein Beispiel für ein Anti-Corruption Layer darstellt.

Das Konzept des Domain Modelling stellt sicher, dass die zugrunde liegende Architektur die Prinzipien der Entkopplung und Modularität beherzigt. Wenn man diese Prinzipien richtig anwendet, führt es zu einer klar strukturierten und wartungsfreundlichen Architektur. Um die Anwendung weiter zu entkoppeln, können Services in einer Event-Driven Architecture (EDA) miteinander kommunizieren. Dies ermöglicht die Interaktion zwischen verschiedenen Komponenten über Event Notification, statt durch direkte Serviceaufrufe, was zu einer noch stärkeren Entkopplung führt.

Das Event-Driven-Modell erweitert das Prinzip der lose gekoppelten Architektur um den Aspekt der zeitlichen Entkopplung. Während in einem klassischen Microservices-Ansatz die Komponenten zum richtigen Zeitpunkt synchronisiert werden müssen, um miteinander zu kommunizieren, erfordert ein Event-Driven-Ansatz keinen synchronen Serviceaufruf mehr. Stattdessen werden Zustandsänderungen durch Events ausgelöst, die von den beteiligten Komponenten asynchron verarbeitet werden. Dieser Paradigmenwechsel führt zu einer höheren Flexibilität, da Komponenten unabhängig voneinander reagieren können, ohne dass eine direkte Kommunikation erforderlich ist.

Ein praktisches Beispiel für die Implementierung eines Event-Driven-Modells könnte ein Onboarding-Prozess für neue Mitarbeiter in einem Unternehmen sein. Jedes Mal, wenn ein neuer Mitarbeiter eingestellt wird, müssen viele verschiedene Systeme aktualisiert werden, etwa das Erstellen einer E-Mail-Adresse, die Aktivierung der Gehaltszahlung oder das Versenden von Willkommenspaketen. In einem traditionellen synchronen Modell müsste ein zentraler Orchestrator diese Aufgaben in einer bestimmten Reihenfolge und in engem Kontakt mit allen beteiligten Systemen steuern. In einem Event-Driven-Modell hingegen genügt es, dass ein Event wie „Neuer Mitarbeiter eingestellt“ ausgelöst wird, auf das dann alle relevanten Systeme individuell reagieren, ohne dass sie voneinander wissen müssen.

Im Gegensatz zur Service-Orchestrierung, die eine zentrale Instanz für das Verwalten von Geschäftsprozessen verwendet, fördert Event-Driven Architecture die Unabhängigkeit der Komponenten und ermöglicht eine dynamische und skalierbare Interaktion zwischen den Microservices. Diese Entkopplung erleichtert nicht nur die Erweiterung und Wartung des Systems, sondern ermöglicht auch eine bessere Skalierbarkeit, da einzelne Komponenten unabhängig voneinander skaliert werden können.

Die Weiterentwicklung dieser Architektur führt zu einer noch stärkeren Flexibilisierung der Softwarelandschaft. Durch die zunehmende Nutzung von Cloud-Technologien und der Einführung von Event Notification können diese Komponenten leichter gehostet und miteinander kommunizieren. Die Entkopplung der Komponenten, sowohl in Bezug auf ihre Implementierung als auch in Bezug auf ihre Ausführungszeit, stellt sicher, dass die Softwarelösung anpassungsfähig und resilient bleibt.

Wichtig zu beachten ist, dass diese Architektur nicht nur technische Vorteile bietet. Sie stellt auch eine Veränderung in der Art und Weise dar, wie Unternehmen Geschäftsprozesse modellieren und automatisieren. Der Übergang von einer monolithischen Struktur hin zu einer verteilten Architektur, in der die Kommunikation durch Events geregelt wird, bietet eine Möglichkeit, Prozesse flexibler und anpassungsfähiger zu gestalten.