Microservice Patterns (Domain Events)

“Der Weise ist auf alle Ereignisse vorbereitet.”

Molière

Wie auch der vorherigen Beitrag handelt dieser über Microservices als Architekturkonzept und die dort verwendeten Design Pattern. Bei dem Microservice Pattern Domain Events handelt es sich um die Nutzung von fachlichen Ereignisse aus dem Bereich Domain-Driven Design. Die Microservices kommunizieren also nicht nur über direkten Nachrichtenaustausch mit ihnen bekannten Services, sondern sie signalisieren auch Änderungen an entkoppelte, ihnen unbekannte Services.

Fachliche Ereignisse sind Objekte, welche komplexe, sich unter Umständen dynamisch ändernde, Aktionen des Domänenmodells beschreiben, die ein oder mehrere Aktionen oder Änderungen in den Fachobjekten bewirken. Fachliche Ereignisse ermöglichen auch die Modellierung verteilter Systeme. Die einzelnen Subsysteme kommunizieren ausschließlich über fachliche Ereignisse, damit werden sie stark entkoppelt und das gesamte System somit wartbarer und skalierbarer.

WIKIPEDIA

Es handelt sich also nicht um rein technische Steuersignale. Vielmehr beinhalten die Domain Events Änderungen die sich aus dem Domänenmodell ergeben. Beispielsweise könnten folgende Domain Events in einer Stammbaumanwendung verwendet werden: Stammbaum erzeugt, DSGVO geprüft, Ahne angelegt, Person verstorben und Person geboren.

Die Domain Events werden von den Microservices genutzt um andere Services auf Änderungen hinzuweisen. Diese können dann auf diese Änderungen gezielt reagieren. Die Benachrichtigung geschieht üblicherweise über einen Message Bus, damit die Mikroservices entkoppelt bleiben. Arbeiten mehrere Mikroservices auf diese Art zusammen, dann nennt man das Choreographie. Vorstellen kann man sich das als Flash Mob vorstellen. Einer gibt das Signal und dann zeigen alle ihre eigeübten Schritte. was im Detail nicht interessant wirkt, zeigt sich aber in der geordneten Gruppenaktivität. Ein Stammbaum-Microservice kann so beispielsweise auf den Domain Event DSGVO geprüft reagieren, um Personen, nach Eingang ihrer Genehmigung, in den Stammbäumen sichtbar zu schalten.

Die folgende Abbildung zeigt die beispielhafte Microservices Architektur aus dem Database Per Service Beitrag. Der Mikroservice 3 ist mit einem schwarzen Briefumschlag markiert und bedeutet, dass dieser Microservice Domain Events nutzt um andere Services zu informieren. Der Mikroservice 1 und der Mikroservice 2 sind mit einem weißen Briefumschlag markiert und reagieren auf spezielle Domain Events.

In dieser Architektur konnten bislang Mikroservice 3 und Mikroservice 4 die Daten aus der Datenbank in einem lokalen Cache halten, da sie durch das Pattern Database Per Service die alleinige Hoheit über die Daten besitzen. Die beiden anderen Microservices kamen bislang nicht in den Genuss, da sie von Mikroservice 3 abhängig sind.

Durch Domain Events sieht die Situation nun aber ganz anders aus. Aktualisiert Mikroservice 3 seine persistierten Daten, dann versendet er einen Domain Event um interessierte Parteien darauf aufmerksam zu machen. Die beiden Microservices können nun auch ihre Daten zwischenspeichern, weil sie Daten die von Mikroservice 3 abhängig sind, durch einem entsprechenden Domain Event aus ihren Speicher entfernen. Bei der nächsten Anfrage werden dann die aktuellen Daten von Mikroservice 3 geholt.

Mit Domain Events lassen sich noch einige weitere interessante Design Pattern realisieren, die sich bemühen, die Nachteile verteilter Datenhaltung zu kompensieren. Wenn sich ein Microservice um die Verwaltung von Stammbäumen kümmert und ein anderer Microservice um die Verwaltung der Mitglieder, dann ist eine Änderungsansicht für Stammbäume, die auch Mitgliederinformationen enthalten sollen, nicht mehr trivial zu realisieren.

Das Zauberwort heißt hier CQRS und meint Command Query Responsibility Segregation. Versenden beide Microservices Domain Events über die Änderungen an ihrer Datenbasis, dann kann ein dritter Microservices diese Domain Events sammeln und Daten für eine Änderungsansicht bereitstellen. Es klingt nicht besonders elegant, ähnliche Daten in verschiedenen Mikroservices bereitzuhalten. Manche Systeme gehen daher einen Schritt weiter und speichern die Entitäten gleich als Liste von Domain Events (Event Sourcing).

Zu guter Letzt gibt es noch ein weiteres Design-Pattern, dass sich die Domain Events zunutze macht. Da es über Microservice Grenzen hinweg keine Transaktionen gibt, können gemeinsame Arbeiten mit dem Saga Design-Pattern realisiert werden. Die an einer Saga beteiligten Microservices hören dabei jeweils auf ein Domain Event eines anderen Microservice.

Die Microservices bilden eine Eimerkette, in der sich die einzelnen Mikroservices ihre Events zuwerfen, bis der letzte in der Reihe seine Aufgabe erfüllt hat. Tritt in der Kette ein Fehler auf, dann korrigieren Microservices die weiter vorne in der Kette stehen ihre Änderungen und der initiale Microservice meldet einen Fehler. In der obigen Abbildung symbolisieren die grünen Pfeile Domain Events, die ein erfolgreiche Bearbeitung symbolisieren. Die nicht erfolgreichen Bearbeitungen sind die roten Pfeile, bei denen die Vorgänger ihre Änderungen rückgängig machen und auch einen Misserfolg weitermelden.

Das Saga Pattern arbeitet gut mit dem Event Sourcing Pattern zusammen, da in den einzelnen Microservices jeweils ein Domain Event ausgelöst wird. Bei Event Sourcing werden genau diese Domain Events gespeichert und können, bei einem Zurückrollen der Saga, effizient entfernt werden.

Wenn nur wenige Microservices im Team spielen sollen, können weniger komplexe Design-Pattern verwendet werden. Dazu aber mehr in einem weiteren Beitrag, wenn sich alles um Gateways und Aggregationen dreht.