Event Driven Architecture
Bausteine reagieren auf Ereignisse
Die Idee und ihr Nutzen
Ein Kunde kündigt bei seiner Versicherung den Hausratvertrag 88123 zum Jahresende. Daraufhin müssen mehrere Programme etwas tun: Das Inkasso muss den Beitragseinzug stoppen, das Kundenportal einen Hinweis anzeigen und der Vertrieb einen Rückruf planen, um den Kunden zurückzugewinnen. Die Verträge verwaltet ein eigenes Programm, der Vertragsdienst. Statt die anderen Programme einzeln aufzurufen, meldet der Vertragsdienst nur einmal „Vertrag 88123 gekündigt zum 31.12.“ an einen Ereigniskanal für Kündigungen. Den Ereigniskanal stellt ein eigenes Programm bereit, das Meldungen entgegennimmt und an andere Programme verteilt. Jedes Programm, das sich bei diesem Ereigniskanal angemeldet hat, erhält eine Kopie der Meldung und reagiert selbstständig darauf. Soll später zusätzlich ein Statistikdienst die Kündigungen zählen, meldet sich der Statistikdienst beim selben Ereigniskanal an, und der Vertragsdienst bleibt unverändert. Eine solche Meldung über etwas Geschehenes heißt Ereignis, und eine Bauweise, in der Programme vor allem über Ereignisse zusammenarbeiten, heißt Event Driven Architecture, auf Deutsch ereignisgesteuerte Architektur.
Wie funktioniert es?
Der Kunde kündigt telefonisch, und ein Sachbearbeiter erfasst die Kündigung des Hausratvertrags 88123 im Vertragsdienst. Der Vertragsdienst speichert die Kündigung und veröffentlicht einmal das Ereignis „Vertrag 88123 gekündigt zum 31.12.“ im Ereigniskanal. Der Vertragsdienst wartet auf keine Antwort und weiß nicht, wer das Ereignis liest. Der Ereigniskanal ist ein Teil der Nachrichtenplattform der Versicherung, eines eigenen Programms, das Meldungen von Programmen entgegennimmt und anderen Programmen zustellt. Der Ereigniskanal stellt jedem angemeldeten Programm eine Kopie des Ereignisses bereit. Das Inkasso trägt ein, dass nach dem Dezemberbeitrag kein Beitrag mehr eingezogen wird. Der Vertrieb legt eine Aufgabe an, den Kunden im November anzurufen, also noch vor dem Vertragsende. Das Kundenportal ist gerade wegen Wartungsarbeiten nicht erreichbar. Nach den Wartungsarbeiten holt das Kundenportal das Ereignis nach, sofern die Nachrichtenplattform es so lange aufbewahrt. Danach zeigt das Kundenportal dem Kunden bei der nächsten Anmeldung „Ihr Vertrag endet am 31.12.“. Ein Jahr später möchte das Controlling Kündigungen je Monat zählen. Ein Entwicklungsteam baut dafür einen Statistikdienst, der sich beim selben Ereigniskanal für Kündigungen anmeldet. Der Vertragsdienst bleibt dabei unverändert.
Vertragsdienst, Inkasso, Kundenportal und Vertrieb sind eigenständige Programme, die verschiedene Teams entwickeln und betreiben, jedes mit eigenen Daten und eigenen Wartungszeiten. Ein Ereignis beschreibt etwas, das bereits geschehen ist, und wird deshalb in der Vergangenheit formuliert: „Schaden gemeldet“, „Vertrag gekündigt“, „Zahlung ausgeführt“. Ein Ereignis ist kein Auftrag an einen bestimmten Empfänger, sondern eine Mitteilung an alle Programme, die sich dafür angemeldet haben. Die Beteiligten haben drei Rollen:
- Erzeuger: Das Programm, bei dem sich etwas geändert hat, veröffentlicht ein Ereignis. Bei der Kündigung ist das der Vertragsdienst.
- Verteiler: Die Nachrichtenplattform nimmt das Ereignis im passenden Ereigniskanal an und stellt jedem angemeldeten Verbraucher eine Kopie bereit. Ob und wie lange Ereignisse aufbewahrt werden, hängt davon ab, welche Nachrichtenplattform verwendet wird und wie sie eingestellt ist.
- Verbraucher: Programme melden sich für die Arten von Ereignissen an, die sie brauchen, und reagieren darauf, sobald sie dazu kommen, das Kundenportal etwa erst nach seinen Wartungsarbeiten. Bei der Kündigung haben das Inkasso, das Kundenportal und der Vertrieb diese Rolle, ein Jahr später auch der Statistikdienst.
Ereignisse gibt es in zwei Formen. Die knappe Form meldet nur, dass sich etwas geändert hat, zum Beispiel „Vertrag 88123 geändert“. Die Einzelheiten holt der Verbraucher dann selbst beim Vertragsdienst ab. Die vollständige Form enthält alle Angaben, die die Verbraucher brauchen. Das Ereignis „Vertrag 88123 gekündigt zum 31.12.“ hat diese Form, denn es enthält auch das Kündigungsdatum. Kein Verbraucher muss deshalb beim Vertragsdienst nachfragen.
Der Vertragsdienst erfährt nicht, ob und wann Inkasso, Kundenportal und Vertrieb das Ereignis verarbeitet haben. In einer ereignisgesteuerten Architektur ist deshalb nicht sichergestellt, dass alle Programme zu einem bestimmten Zeitpunkt denselben Stand kennen. Während der Wartungsarbeiten weiß das Inkasso schon von der Kündigung, das Kundenportal noch nicht. Ein Verbraucher kann eine Kopie auch zweimal erhalten. Das geschieht zum Beispiel, wenn der Verbraucher den Empfang bestätigt, diese Bestätigung aber nicht bei der Nachrichtenplattform ankommt und die Nachrichtenplattform die Kopie noch einmal zustellt. Jeder Verbraucher muss deshalb erkennen können, dass er auf diese Kündigung schon reagiert hat, etwa weil er die Kennung des Ereignisses bei der ersten Verarbeitung gespeichert hat.
Ereignisgesteuert: eine Kündigung, mehrere Reaktionen
Wann ist es sinnvoll und wann nicht?
Eine Event Driven Architecture lohnt sich
- Viele Programme sollen auf dieselben Änderungen reagieren. Ohne Ereignisse müsste der Vertragsdienst Inkasso, Kundenportal und Vertrieb direkt nacheinander aufrufen. Der Vertragsdienst müsste dafür ihre Adressen kennen, auf ihre Antworten warten und mit ihren Fehlern umgehen.
- Programme sollen unabhängig voneinander erweitert werden. Ohne Ereignisse müsste das Team des Vertragsdienstes für jedes weitere Programm, das von Kündigungen erfahren soll, den Code des Vertragsdienstes ändern und einen weiteren Aufruf einbauen. Ändert eines dieser Programme seinen Aufruf, müsste das Team des Vertragsdienstes auch den Vertragsdienst ändern, obwohl der Vertragsdienst mit der Aufgabe dieses Programms nichts zu tun hat.
- Programme sollen unabhängig voneinander betrieben werden, und einzelne Programme dürfen zeitweise nicht erreichbar sein. Bei direkten Aufrufen scheitert oder verzögert sich die ganze Kündigung, wenn das Kundenportal gerade nicht erreichbar ist. Mit Ereignissen kann jedes Team sein Programm zu eigenen Zeiten ausliefern und Wartungsarbeiten planen, solange der Aufbau des Ereignisses gleich bleibt.
- Die Reaktionen müssen nicht sofort geschehen. Ob der Vertrieb den Rückruf heute oder morgen plant, ändert nichts an der Kündigung selbst. Bei direkten Aufrufen wartet der Sachbearbeiter trotzdem, bis Inkasso, Kundenportal und Vertrieb geantwortet haben. Mit Ereignissen ist die Kündigung erfasst, sobald der Vertragsdienst sie gespeichert und das Ereignis veröffentlicht hat.
Eine Event Driven Architecture ist nicht das Richtige
- Ein Programm braucht sofort eine Antwort von einem anderen Programm. Ein Beispiel ist das Kundenportal, das einen Beitrag berechnen lässt, während der Kunde auf das Ergebnis wartet. Ein Ereignis meldet nur etwas Geschehenes und bringt keine Antwort zurück. Mit Ereignissen bräuchte es deshalb ein Ereignis für die Anfrage und ein zweites für die Antwort, und das Kundenportal müsste die Antwort der richtigen Anfrage zuordnen. Stattdessen ruft das Kundenportal das Programm für die Beitragsberechnung direkt auf und wartet auf die Antwort, nach dem Muster Request Reply.
- Mehrere Änderungen müssen in verschiedenen Programmen gemeinsam gelingen oder gemeinsam scheitern. Ein Beispiel ist eine Beitragserstattung, bei der das Inkasso den Betrag zurückzahlt und der Vertragsdienst die Erstattung am Vertrag vermerkt. Ereignisse bringen die Programme nur nach und nach auf denselben Stand. Dieses Verhalten heißt Eventual Consistency. Dann ändern die Programme ihre Daten gemeinsam nach dem Verfahren Two Phase Commit: Ein steuerndes Programm fragt zuerst alle Beteiligten, ob sie die Änderung übernehmen können. Erst wenn alle zugesagt haben, übernehmen alle die Änderung. So sieht kein Programm einen Stand, in dem nur ein Teil der Änderungen übernommen ist.
- Ein einzelner Ablauf mit festen Schritten soll zentral gesteuert und überwacht werden. Bei Ereignissen ist der Ablauf in keinem einzelnen Programm beschrieben, und wer wissen will, wie weit ein Vorgang ist, müsste bei jedem Verbraucher nachsehen. Stattdessen beauftragt ein steuerndes Programm die Beteiligten der Reihe nach, nach dem Muster Orchestration. Der Ablauf ist dann im steuernden Programm beschrieben, und das steuernde Programm kennt alle Beteiligten. Den Stand eines Vorgangs fragt man beim steuernden Programm ab.
- Nur zwei Programme tauschen Daten aus, etwa der Vertragsdienst und das Inkasso, und daran wird sich absehbar nichts ändern. Ein Ereigniskanal hätte dann immer nur einen Empfänger, und kein weiteres Programm würde sich anmelden. Dann ruft der Vertragsdienst das Inkasso direkt auf. Ist das Inkasso nicht erreichbar, wiederholt der Vertragsdienst den Aufruf später selbst. Eine Nachrichtenplattform, die betrieben und überwacht werden muss, entfällt.
Nachteile und typische Fehler
Der Gesamtablauf ist in keinem einzelnen Programm beschrieben. Wer wissen will, was nach einer Kündigung alles passiert, muss nachsehen, welche Programme sich für das Ereignis angemeldet haben und was sie tun. Für die Fehlersuche brauchen deshalb alle Ereignisse eines Vorgangs eine gemeinsame Kennung, und eine Überwachung muss die Ereignisse über alle Programme hinweg verfolgen. Verbraucher erfahren von Änderungen mit Verzögerung, und wie lang diese Verzögerung höchstens ist, lässt sich nicht zusagen. Scheitert eine Zustellung oder ist ein Verbraucher lange nicht erreichbar, muss eine Überwachung das melden, und die Zustellung muss nachgeholt werden. Die Nachrichtenplattform ist ein zusätzliches System, das betrieben, gesichert und überwacht werden muss. Weil der Erzeuger seine Verbraucher nicht kennt, braucht es für Änderungen am Aufbau eines Ereignisses Versionen und feste Regeln. Eine solche Regel kann lauten, dass neue Felder hinzukommen dürfen, aber kein vorhandenes Feld umbenannt wird.
Typische Fehler und woran man sie erkennt:
- Verlorenes Ereignis: Der Vertragsdienst speichert die Kündigung, stürzt aber ab, bevor er das Ereignis veröffentlicht. Inkasso, Kundenportal und Vertrieb erfahren nie davon, und das Inkasso zieht im Januar weiter den Beitrag ein. Erkennbar ist das an gekündigten Verträgen, zu denen kein Ereignis im Ereigniskanal steht.
- Doppelt verarbeitet: Der Vertrieb bestätigt den Empfang der Kündigung, die Bestätigung kommt aber nicht bei der Nachrichtenplattform an. Die Nachrichtenplattform stellt dem Vertrieb die Kopie deshalb noch einmal zu. Der Vertrieb prüft nicht, ob er auf diese Kündigung schon reagiert hat, und legt für denselben Kunden eine zweite Aufgabe an, ihn anzurufen. Erkennbar ist das an doppelten Einträgen mit derselben Vertragsnummer.
- Falsche Reihenfolge: Ereignisse können in anderer Reihenfolge ankommen, als sie veröffentlicht wurden, etwa wenn die Nachrichtenplattform eine Kopie nach einer gescheiterten Zustellung erst später zustellt. Der Kunde nimmt die Kündigung am nächsten Tag zurück. Beim Kundenportal kommt „Kündigung zurückgenommen“ vor „Vertrag gekündigt“ an, und das Kundenportal zeigt den Vertrag danach als gekündigt. Erkennbar ist das an Programmen, die für denselben Vertrag unterschiedliche Stände zeigen.
- Geänderter Aufbau: Das Team des Vertragsdienstes benennt das Feld für das Kündigungsdatum im Ereignis um. Der Statistikdienst liest dieses Feld, aber vom Statistikdienst weiß das Team des Vertragsdienstes nichts. Nach der Auslieferung findet der Statistikdienst das Feld nicht mehr und kann die Ereignisse nicht mehr verarbeiten. Erkennbar ist das an Fehlermeldungen beim Verbraucher kurz nach einer Auslieferung des Vertragsdienstes.
- Versteckter Auftrag: Der Vertragsdienst veröffentlicht „Beitragseinzug stoppen“ als Ereignis und verlässt sich darauf, dass das Inkasso den Einzug stoppt. Ist das Inkasso gestört und verarbeitet das Ereignis nicht, bemerkt der Vertragsdienst das nicht, weil er auf keine Antwort wartet. Das Inkasso zieht den Beitrag dann weiter ein. Erkennbar ist das an Namen von Ereignissen in Befehlsform wie „Beitragseinzug stoppen“.
Weiterführend
Verwandte Ansätze
- Publish Subscribe: beschreibt, wie eine Nachrichtenplattform eine Meldung an alle angemeldeten Verbraucher verteilt.
- Choreography: Abläufe, die sich allein daraus ergeben, dass Programme auf Ereignisse reagieren und neue Ereignisse melden.
- Event Sourcing: speichert den Zustand eines Programms selbst als Folge von Ereignissen. Der Vertragsdienst würde dann statt des aktuellen Vertrags die Ereignisse „Vertrag abgeschlossen“ und „Vertrag gekündigt“ speichern.
- Outbox Pattern: Vorgehen, bei dem der Erzeuger das Ereignis zusammen mit seinen Daten in der eigenen Datenbank speichert und ein eigener Programmteil das Ereignis von dort veröffentlicht. Arbeitet der Vertragsdienst so, geht das Ereignis zur Kündigung auch dann nicht verloren, wenn der Vertragsdienst zwischen Speichern und Veröffentlichen abstürzt.
Werkzeuge und Umsetzungen
- Apache Kafka: Plattform, die Ereignisse für eine festgelegte Dauer speichert. Kafka teilt jeden Kanal in Teilbereiche, sogenannte Partitionen. Innerhalb einer Partition bleibt die Reihenfolge erhalten. Über alle Partitionen hinweg ist keine Reihenfolge zugesagt.
- Apache Pulsar: Plattform für Ereignisse und Warteschlangen, die die Speicherung auf eigene Server auslagert.
- NATS: Nachrichtenplattform, die Ereignisse mit ihrer Erweiterung JetStream auch dauerhaft speichern kann.
- Knative Eventing: leitet Ereignisse an angemeldete Dienste weiter, die in Kubernetes laufen. Kubernetes ist eine verbreitete Plattform zum Betrieb von Anwendungen auf vielen Servern.
Quellen
- Martin Fowler: What do you mean by “Event-Driven”?: unterscheidet die knappe Benachrichtigung von Ereignissen mit vollständigen Angaben und nennt als Risiko, dass der Gesamtablauf nirgends im Code sichtbar ist.
- Microsoft: Event-driven architecture style: beschreibt Erzeuger, Kanal und Verbraucher, die Einsatzfälle und Schwierigkeiten wie doppelte Zustellung, Reihenfolge, Fehlerbehandlung und Versionen von Ereignissen.