Der Kleber macht’s – Microservices in einem Web-Projekt
Auch bei Webprojekten ist es sinnvoll, mit modularisierten Teilsystemen zu arbeiten. Fehler bei der Implementierung können aber zu trägen Modulithen führen.
(Bild: erstellt mit Midjourney durch iX)
- Max Schröter
In der modernen Softwareentwicklung haben sich Microservices im Allgemeinen und Self-contained Systems als spezieller Ansatz für das Web etabliert. Module zu eigenständigen Deployment-Artefakten zu machen, verspricht technische Vorteile: unabhängige Deployments, gezielte Skalierbarkeit und bessere Wartbarkeit.
Doch die technische Trennung hat auch Nachteile: Zum einen können Module, die Teil eines System of Systems sind, per Definition nicht unabhängig voneinander existieren. Zum anderen müssen einmal auseinandergeschnittene Systeme für Benutzerinnen und Benutzer wieder zusammengeführt werden, um eine möglichst angenehme, einheitliche User-Experience zu schaffen.
Verteilte Systeme wie Self-contained Systems sprechen demnach aus verschiedenen Gründen miteinander. Um die erforderliche technische Kommunikation zu ermöglichen, gibt es mehrere Lösungsansätze, und es ist nicht trivial, für den konkreten Anwendungsfall den richtigen zu finden.
Videos by heise
CAP der guten Hoffnung
Das CAP-Theorem (siehe Abbildung 1) besagt, dass verteilte Systeme nur zwei der drei wünschenswerten Eigenschaften Konsistenz (Consistency), Verfügbarkeit (Availability) und Ausfallsicherheit (Partition Tolerance) erfüllen können.
- Consistency beschreibt die Garantie, dass jeder Knoten des verteilten Systems den gleichen Zustand eines Datensatzes kennt.
- Availability bedeutet, dass jeder nicht ausfallende Knoten auf alle Lese- und Schreibzugriffe in einer angemessenen Zeitspanne antworten muss.
- Partition Tolerance besagt, dass ein nicht ausfallender Knoten trotz des Ausfalls eines oder mehrerer anderer Knoten weiterhin arbeiten muss.
Da die Einhaltung von Konsistenz- und Verfügbarkeitsgarantien bei nicht gewährleisteter Ausfallsicherheit gerade bei Webprojekten irrelevant ist, ist die oben genutzte populäre Beschreibung des Theorems in der Praxis irreführend. Die zu treffende Entscheidung besteht letztlich darin, ob das System in einem Ausfallszenario Consistency oder Availability gewährleistet.
Bei der Entscheidung für einen bestimmten Integrationsmechanismus zwischen Modulen in einem System sollte das CAP-Theorem eine Rolle spielen, denn es verhindert eine unterkomplexe Sicht auf die Welt verteilter Systeme. Tägliche Ausfälle von Netzwerkknoten gehören zum Alltag von IT-Abteilungen und sollten von Softwareschaffenden nicht ignoriert werden.
Die Auswirkungen von Kopplungen
Eine starke Kopplung führt dazu, dass eine Änderung in einem Modul weitere Änderungen in anderen nötig macht. Was schon in monolithischen Systemen ein Problem ist, zeigt sich in verteilten und verteilt entwickelten Systemen noch deutlicher: Starke Kopplung erfordert hohen Kommunikationsaufwand und führt zu längeren Release-Zyklen. Eine Microservice-Architektur entwickelt sich durch zu starke Kopplung der einzelnen Systeme miteinander zu einem verteilten Monolithen, der die schlechten Eigenschaften beider Architekturansätze miteinander vereint.
Ein Modul kann als Teil eines System of Systems nicht vollkommen unabhängig existieren, der Grad der Kopplung sollte aber so klein wie möglich sein. Da die Entscheidung für einen Integrationsmechanismus den Grad maßgeblich beeinflussen kann, ist eine sorgfältige, auf ausgesuchten Qualitätskriterien basierende Entscheidungsfindung enorm wichtig.
Abbildung 2 zeigt am Beispiel von Shared Code, wie der Einsatz spezifischer Technologie den initialen Aufwand verringert, dafür aber den Grad an Kopplung erhöht. Den initialen Aufwand gering zu halten, ist ein Beispiel für ein schlecht gewähltes Qualitätskriterium, da dies ein einmaliger Effekt ist und sich negativ auf die mittel- bis langfristige Entwicklung auswirkt.
Lauf- oder Entwicklungszeit?
Es gibt viele verschiedene Ansätze, verteilte Systeme miteinander zu integrieren. Sehr abstrakt lassen sie sich in zwei Kategorien unterteilen: Während die Integration zur Entwicklungszeit in Bezug auf das CAP-Theorem eine klare Entscheidung für das Merkmal Availability ist, gilt es bei der Integration zur Laufzeit weiter zu differenzieren.
Die Integration zur Entwicklungszeit geschieht in der Regel über geteilten Code. Man stellt entweder Bibliotheken mit einem Paketmanager zur Verfügung oder nutzt Versionskontrollsysteme. Das löst Abhängigkeiten auf, bevor ein Deployment-Artefakt des integrierenden Systems in Produktion geht – spätestens während des Build-Prozesses.
Da es sich bei der Integration zur Entwicklungszeit um eine klare Entscheidung für das Merkmal Availability handelt, garantiert das System keine Konsistenz über die integrierten Inhalte; deren Aktualisierung geschieht frühestens mit der nächsten Auslieferung in die Produktion.
Ein typischer Anwendungsfall dieser Art der Integration stellt die Nutzung einer systemübergreifenden Pattern-Library dar, die den Teilsystemen UI-Komponenten zur Verfügung stellt. Das führt zu einheitlicher User-Experience über alle Teilsysteme hinweg und senkt den Aufwand bei den Entwicklungsteams. Hier ist die Konsistenz hoch, da unterschiedliche Farbtöne oder die Positionierung eines Buttons wenig Einfluss auf die Funktionsfähigkeit der Software haben.
Mechanismen zur Laufzeitintegration lassen sich in zwei weitere Kategorien unterteilen: synchron und asynchron. Beide haben ein Merkmal gemeinsam: Sie lösen Abhängigkeiten erst während des Betriebs in einer Laufzeitumgebung auf. Daraus folgt ein etwas höherer Kopplungsgrad im Vergleich zu dem Kopplungsgrad, der bei einer Integration während der Entwicklungszeit entsteht. Denn Änderungen an der Schnittstelle eines Systems können das andere System beeinträchtigen, ohne dass das bereits während des Entwicklungs- oder Deployment-Prozesses erkannt wird.
Wird ein System asynchron zur Laufzeit integriert, geschieht das zeitlich unabhängig von User-Interaktionen. Auslöser können beispielsweise Benachrichtigungen über Ereignisse in anderen Systemen oder geplante Zeitintervalle sein. Nach dem Auslösen greift das integrierende Modul Daten des zu integrierenden Moduls ab und übersetzt sie in ein Modell, das im Hinblick auf funktionale und cross-funktionale Anforderungen auf seinen Bedarf zugeschnitten ist.
Die Entscheidung für asynchrone Laufzeitintegration ist eine Entscheidung für das Merkmal Availability. Für eine hohe Verfügbarkeit und – im Gegensatz zu synchroner Laufzeitintegration – geringe Laufzeitkopplung wird Eventual Consistency in Kauf genommen. Das bedeutet, dass Konsistenz nicht sofort vorhanden ist, aber dafür das Versprechen auf einen konsistenten Zustand über Module hinweg zu einem Zeitpunkt in der Zukunft. Dies kann situationsabhängig innerhalb von Millisekunden, Minuten oder Stunden der Fall sein.
Synchrone Laufzeitintegration ist im Gegensatz zu ihrer asynchronen Schwester häufig an die User-Interaktionszeit gekoppelt. Jede Interaktion einer Benutzerin oder eines Benutzers mit dem System stellt einen Auslöser dar: Sei es das Laden einer Website oder der Klick auf einen Hyperlink. Nach dem Eintreten des Auslösers lädt das System beispielsweise frontendseitig Inhalte aus einem anderen System nach oder schickt gar eine ganze Reihe an logisch aufeinander aufbauenden Anfragen an andere Systeme. Eine Entscheidung für eine synchrone Laufzeitintegration ist eine für Consistency, denn jede Anfrage hat aufgrund des Verhaltens der zu integrierenden Module Einfluss auf Availability. Die Auswirkung auf die Laufzeitkopplung zwischen den Modulen ist höher und kann abhängig vom spezifischen Integrationsmechanismus marginal bis dramatisch sein.
(Bild: iX)
Dieser Artikel ist auch im iX/Developer-Sonderheft zu finden, das sich an Softwarearchitektinnen und Softwarearchitekten richtet. Neben den klassischen Architekturinhalten zu Methoden und Pattern gibt es Artikel zu Soziotechnischen Systemen, Qualitätssicherung oder zu Architektur und Gesellschaft. Domain Driven Design ist ebenso ein Thema wie Team Topologies und Sicherheit.
Als Autoren konnten wir bekannte Experten gewinnen, die ihr Wissen in vielen spannenden Artikeln – wie dem hier vorliegenden – sowohl für Architektureinsteiger als auch Spezialisten weitergeben.