Eine unserer Hauptaufgaben als Architekt:in ist es, ein passendes Systemdesign zu entwickeln, das die Erfüllung der Anforderungen an das System (insbesondere die Qualitätsanforderungen) erlaubt. Jedes System ist einzigartig und es müssen immer eigene, spezifische Architekturkonzepte entworfen werden. Aber um das gut tun zu können, ist es wichtig, als Architekt:in einen gut gefüllten Werkzeugkoffer dabei zu haben, mit unterschiedlichen Ansätzen, Mustern und Taktiken. Außerdem braucht man ein gutes Verständnis dafür, welcher Ansatz sich für welche Problemstellung und Kontextfaktoren eignet und was Vorteile und Tradeoffs sind. Für unsere Arbeit lohnt es sich also enorm, viel zu lesen und Talks zu schauen, um von den Erfahrungen anderer zu profitieren und damit seinen Werkzeugkoffer zu füllen.
Einige dieser Talks, Artikel oder Bücher haben einen nachhaltigen Eindruck bei mir hinterlassen, weil sie ein Thema besonders gut erklären, strukturieren und einordnen. Ein Beispiel dafür ist der Talk von Martin Fowler zum Thema „Event-Driven Architecture“ von der GOTO Chicago aus dem Jahr 2017. Vielleicht kennen einige von Euch den Talk schon; mit über 671k Views gehört er zu den populärsten Vorträgen im YouTube-Kanal von GOTO.
In diesem Artikel möchte ich Euch die aus meiner Sicht wichtigsten Kernpunkte kurz zusammenfassen. Als schnelle Referenz oder Erinnerung für den Arbeitsalltag – oder als Appetizer, falls ihr den Talk noch nicht kennt.
Was heißt Event-Driven?
Der Begriff ist nicht klar definiert. Wenn jemand sagt: „Wir haben ein eventgetriebenes System“, kann das vieles bedeuten. Martin Fowler unterscheidet deshalb vier Muster, von denen mindestens eines umgesetzt ist, wenn jemand von einem eventgetriebenen System spricht:
Event Notification
Direkte Aufrufe zwischen Subsystemen oder Services zur Interaktion werden bei Event Notification ersetzt durch Ereignisse, die von Services publiziert und von anderen verarbeitet werden. Dies ist eine ganz substanzielle Veränderung der Sichtweise, die mit einigen Implikationen einhergeht:
- Es verändert sich die Abhängigkeitsbeziehung zwischen Systemteilen. Bei direkten Aufrufen, muss der aufrufende Service den aufgerufenen Services kennen. Dadurch ergibt sich eine explizite Kopplung. Bei Event Notification dreht sich die Abhängigkeit: Ein Service publiziert ein Ereignis, ohne eine Annahme über dessen weitere Verarbeitung zu machen. Andere Services entscheiden selbst, ob das Ereignis für sie relevant ist und ob sie es verarbeiten möchten. Die Abhängigkeit ist natürlich nicht vollständig weg, die Systemteile müssen noch immer zusammenarbeiten. Die Richtung der Abhängigkeit dreht sich aber und wird gleichzeitig weniger eng.
- Aufrufe sind transient, Ereignisse hingegen sind ein explizites „Ding“, das man speichern, verarbeiten, nutzen etc. kann.
- Ereignisse sind keine Befehle (Commands). Während Aufrufe typischerweise als Commands realisiert sind („Speichere das!“), geben Events anderen Systemteilen nur Aufschluss, welche wichtigen Ereignisse passiert sind („Erika Müllers Adresse hat sich geändert.“). Diesen Unterschied muss man ganz explizit in deren Gestaltung und Benennung beachten.
Mit diesem Ansatz sind wir in der Lage, sehr gut entkoppelte Systeme zu bauen, zu denen auch sehr leicht neue Systemteile hinzugefügt werden können. Man erhält damit viel Erweiterbarkeit und Flexibilität für die Zukunft.
Es gibt aber auch Tradeoffs: Im Normalfall brauche ich ein zusätzliches Stück Infrastruktur, das als Event Broker dient, das man auswählen und betreiben muss. Außerdem büßt man ein gutes Stück Verständlichkeit ein, weil es bei diesem Ansatz keine Möglichkeit gibt, einfach im Code zu lesen, was passiert. Um das Systemverhalten ganzheitlich zu verstehen, ist eine Analyse des Event-Flusses zur Laufzeit notwendig. Das macht eine gute Dokumentation umso wichtiger.

Event-Carried State Transfer
Services, die miteinander interagieren, brauchen auch Daten voneinander. Wenn ein Service also im Rahmen von Event Notification das Event „Erika Müllers Adresse hat sich geändert.“ verarbeitet, muss er nachfolgend logischerweise auch die neue Adresse in Erfahrung bringen. Dazu muss er die entsprechenden Daten abrufen (Command: „Gib mir die neue Adresse!“). Kernidee von Event-Carried State Transfer ist es, diese zusätzliche Kommunikation einzusparen, indem die weiteren Daten einfach als Teil des Events versendet werden. Im Beispiel also die neue Adresse. Man möchte damit ganz explizit die direkte Kommunikation zwischen zwei Systemteilen verhindern.
Das bedeutet aber auch, dass das Subsystem, welches das Event verarbeitet, alle Daten, die es für die eigene Arbeit benötigt, aus den Events herausliest und selbst speichert. Wir kommen hier also zu einer Shared-Nothing-Architektur, wie sie im Bereich der Microservices häufig umgesetzt wird.
Dieser Ansatz sorgt für noch mehr Unabhängigkeit der Systemteile voneinander. Das ist ein großer Vorteil, weil ein Service problemlos weiterarbeiten kann, selbst wenn ein anderer gerade down ist. Super für Verfügbarkeit. Aber auch die Performance profitiert davon, wenn Services nicht synchron miteinander sprechen müssen.
Auf der anderen Seite ist die Umsetzung der genannten Datenreplikation einiges an Arbeit. Außerdem sind wir damit notwendigerweise im Bereich Eventual Consistency und müssen uns folglich mit der gesamten Komplexität beschäftigen, die damit einhergeht. Und das ist unter Umständen nicht wenig.

Event Sourcing
Die Kernidee von Event Sourcing ist, nicht primär den Applikationszustand (die Werte aller Daten) persistent zu speichern, sondern ein Log von allen Änderungen zu pflegen, die über die Zeit passiert sind. Der Applikation State kann durch den Anfangszustand und die Anwendung aller Änderungen jederzeit errechnet werden. In der Anwendung funktioniert der Ansatz meistens so, dass bei der Verarbeitung von Events direkt auch gleich der neue Applikationszustand errechnet und kontinuierlich im Speicher gehalten wird. Häufig werden zusätzlich auch Snapshots gemacht und gespeichert, die als neue Basis für die Berechnung des Systemzustands verwendet werden (damit man nicht so viel von vorne berechnen muss).
Event Sourcing hat einige, durchaus gewichtige Vorteile: Mit Speicherung der Events bekommt man gleich ein Audit Log mit, das heißt, man kann sehr gut nachvollziehen, was alles über die Zeit passiert ist. Man kann einfach vergangene Zustände wiederherstellen und alternative Zustände analysieren. Die Möglichkeit, den vollständigen Systemzustand im Speicher zu halten und Änderungen im Speicher zu verarbeiten, erlaubt es, wirklich hochperformante Systeme zu bauen. Und das Programmiermodell (ohne ständiges Ping-Pong mit der Datenbank) ist sehr effizient.
Es gibt aber auch Nachteile: Wichtig ist hier insbesondere, dass dieser Ansatz für viele beteiligte Personen sehr ungewohnt ist und damit gefühlt einige Komplexität einhergeht. Darüber hinaus gibt es einiges zu tüfteln bei Schema-Änderungen von Events oder wenn die Event-Verarbeitung von der Interaktion mit externen Systemen abhängt.

CQRS
CQRS steht für „Command Query Responsibility Segragation“ und bedeutet, dass man dedizierte und voneinander getrennte Datenmodelle für Lese- und Schreiboperationen in der Applikation vorhält. Diese Modelle sind für den jeweiligen Zweck optimiert, so dass sowohl Schreib- als auch Leseoperationen möglichst effizient und schnell durchgeführt werden können. Während der Laufzeit werden typischerweise werden Schreiboperationen verarbeitet und asynchron dazu das Lesemodell daraufhin angepasst.
CQRS würde man beispielsweise einsetzen, wenn man einerseits wenige Schreiboperationen hat, aber andererseits millionenfache Leseoperationen pro Sekunde unterstützen muss. In so einem Fall können optimierte Modelle sinnvoll sein. Allerdings bringt auch dieser Ansatz eine deutliche Zunahme an Komplexität und Entwicklungsaufwand mit. Man sollte den Einsatz also gut abwägen.

Fazit
Ich hoffe, das ist als kurze Zusammenfassung hilfreich. Alle Details findet ihr natürlich im Vortrag. Wenn Ihr den Vortrag noch nicht gesehen habt, solltet ihr jetzt alles stehen und liegen lassen und das Video schauen; es gibt wenig besser angelegte 50 Minuten.
Habt ihr auch solche Artikel oder Talks, die so einen nachhaltigen Eindruck bei Euch hinterlassen haben, dass sie helfen, Euren Werkzeugkoffer zu füllen?
Dominik
0 Kommentare