Altbewährte Entwurfsmuster für zeitgemäße Microservices-Anwendungen

Zahlreiche bekannte Patterns lassen sich erfolgreich auf Microservices-Anwendungen übertragen.

Lesezeit: 4 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen 20 Beiträge
Von
  • Annegret Junker
Inhaltsverzeichnis

Seit langer Zeit dienen Lösungsmuster dazu, wiederkehrende Aufgaben in der Softwareindustrie anzugehen. Aktuelle Architektur-Patterns ähneln zum Teil den Entwurfsmustern aus den 80er und 90er Jahren. Auch in der Microservices-Welt müssen Teams nicht alles neu erfinden, sondern können auf bewährte Muster zurückgreifen, die lediglich neu zu interpretieren sind. Letztlich helfen Entwurfsmuster, schnell und verlässlich für komplexe Fragen möglichst gute und einfache Antworten zu finden.

Daher lohnt es sich zu betrachten, wie sich aus der Softwareentwicklung bekannte Entwurfs- und Architekturmuster für Microservices-Architekturen anwenden lassen. Dabei gilt es, bekannte Entwurfsmuster hinsichtlich Ähnlichkeiten bei Architekturmustern für Microservices zu untersuchen.

Entwurfs- und Architekturmuster sollen helfen, gute Software zu produzieren. Sie unterstützen beim Finden von Ansätzen, die bewährten Prinzipien wie lose Kopplung, Umkehrung der Kontrolle oder dem Vermeiden von Wiederholungen folgen.

Entwurfsmuster (Design Patterns) sind bewährte Lösungsschablonen für wiederkehrende Entwurfsprobleme in der Softwarearchitektur. Sie sind nach Erzeugungs-, Struktur- und Verhaltensmuster unterteilt.

Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides veröffentlichten 1994 das Buch "Design Patterns – Elements of Reusable Object-Oriented Software" [1], in dem sie 23 Entwurfsmuster beschreiben. Mit dem Buch haben sie dem grundsätzlichen Konzept der Patterns zum Durchbruch verholfen. Die vier Autoren sind inzwischen unter dem Spitznamen Viererbande (Englisch: Gang of Four oder kurz GoF) bekannt.

Auf der Grundlage hat sich seitdem ein ganzer Kosmos aus Mustern ergeben, den unter anderem Frank Buschmann mit seiner Arbeit und Beteiligung an der Serie "Pattern-Oriented Software Architecture" [2] verbreitet hat.

Architekturmuster beschäftigen sich mit den Elementen der Architektur einer Anwendung und somit mehr mit Diensten als mit Klassen. Die Unterscheidung zwischen Architekturmustern und Entwurfsmustern ist nicht immer ganz scharf.

Dieser Artikel versteht unter Architekturmuster insbesondere Muster der Microservices-Welt. Er vergleicht sie mit den Mustern der klassischen objektorientierten Programmierung und stellt sie anhand bewährter Architekturprinzipien vor. Letztere dienen der Orientierung. Es bedeutet nicht, dass einzelne Muster jeweils nur ein bestimmtes Prinzip implementieren.

Die Muster spiegeln die Trennung von Verantwortlichkeiten wider (Abb. 1).

Das Prinzip der eindeutigen Verantwortung steht in der Softwareentwicklung dafür, dass eine Klasse, ein Modul oder ein Service für eine Funktion verantwortlich ist.

Als zugehörige Architekturmuster seien die Ansätze Microservices und Monolith genannt. Sie entsprechen den Patterns Multiton und Singleton. Ersterer existiert nicht in der Liste der ursprünglichen GoF-Muster, sondern dient in dem Zusammenhang als Kontrast zum Singleton. Eigentlich sind Monolithen und Singletons Anti-Patterns, die sich jedoch in bestimmten Konstellationen nicht vermeiden lassen. Einzelne Funktionen sollten in Module gekapselt und nur über Schnittstellen anzusprechen sein. Ein Monolith widerspricht diesem Ansatz.

Das Prinzip der Trennung von Zuständigkeiten hat Edsger Dijkstra 1982 als "Separation of Concerns" (Trennung der Zuständigkeiten) beschrieben [3]. Die Anforderung legt fest, dass Teams unterschiedliche Funktionen voneinander trennen müssen, um beherrsch- und pflegbare Software zu entwickeln. In der Microservices-Welt implementieren die Muster Serverless Computing und ein Dienst pro Container das Vorgehen.

Ein Serverless Code Pattern bezeichnet Funktionen, bei denen der jeweilige Cloud-Anbieter Deployment- und Skalierungsverantwortung übernimmt. Es erinnert an das Entwurfsmuster Fliegengewicht, das eine Schablone beschreibt, in die Teilzustände vieler gleichartiger Objekte ausgelagert sind, um unverhältnismäßigen hohen Speicherverbrauch zu vermeiden. Das ist ein ähnlicher Ansatz wie für eine Serverless-Anwendung, bei der die Verantwortung für den Betrieb ausgelagert ist.

Ein Dienst pro Container bedeutet, dass jeder Docker-Container genau einen Dienst bereitstellt. Letzterer existiert nur innerhalb des Containers und ist von außen ausschließlich über Schnittstellen erreichbar. Das Bereitstellungsmuster erinnert an das Container-Pattern, das nicht Bestandteil der ursprünglichen GoF-Muster ist. Der Begriff Container kommt dabei nicht aus dem Docker-Universum, sondern steht in der objektorientierten Programmierung für eine Hülle, um auf große strukturierte Objekte wie Listen oder Bäume zuzugreifen.

Die Diagramme zeigen die Muster für Lose Kopplung und Geheimnisprinzip (Abb. 2).

Aus der Anforderung, Verantwortlichkeiten zu trennen, erfolgt die der losen Kopplung. Letztere bedeutet, dass Module ihre innere Implementierung nicht kennen und nur über Schnittstellen gegenseitig zugreifen. Darüber lässt sich Resilienz erreichen, da die einzelnen Module in der Lage sind, auf Fehler von abhängigen Modulen zu reagieren. Das Prinzip der losen Kopplung unterstützen viele Muster, darunter Beobachter, Besucher, Interceptor und Interpreter.

Sonderheft Moderne Softwarearchitektur

Der vorliegende Artikel stammt aus dem neuen iX-Developer-Sonderheft "Moderne Softwarearchitektur", das am 20. August in gut sortierten Kiosken und Buchhandlungen für 14,90 Euro erscheint. Die PDF-Version ist bereits für 12,99 Euro im Heise Shop verfügbar, und dort lässt sich auch die gedruckte Version bestellen.

Das Sonderheft behandelt auf 148 Seiten die Themenkomplexe Softwarearchitektur, Cloud, Qualitätssicherung und Trends. Es enthält unter anderem Artikel zu Domain-drive Design, Microservices, Kubernetes, Service-Meshes und Quanten-Computing.

Das Pattern Beobachter beschreibt, wie Systeme Änderungen an vielen Objekten beobachten und weitergeben können, ohne dass die Objekte stark gekoppelt sein müssen. Ähnliches lässt sich mit einem Event Broker erreichen, der die Ereignisse an die Module vermittelt, die sie abonniert haben. Da die Module die Ereignisse abholen, entsteht durch die asynchrone Kommunikation eine hohe Entkopplung von Sender und Empfänger.

Auch für eine lose Kopplung gilt es, den Versand von Nachrichten zu garantieren. Das At-Least-Once-Prinzip kann ein transaktionaler Postausgang sicherstellen. Das Vorgehen erinnert an das Kommando-Pattern der objektorientierten Programmierung. Änderungen landen sowohl in der passenden Tabelle als auch im Postausgang. Ein Message Relay liest anschließend die Nachrichten im Postausgang und reicht sie an den Message Broker weiter. Somit entsteht Verlässlichkeit bei der Weitergabe von Änderungen, ohne dass eine transaktionale Klammer über mehrere Module gespannt sein muss.