BPMN (Teil 2): Eine Saga von Microservices

BPMN (Teil 2): Eine Saga von Microservices

Im ersten Teil dieser Artikelserie zur BPMN haben wir einen Blick auf die BPMN geworfen, BPMN und Microservices diskutiert und am Beispiel einer Antragsverteilung einen vereinfachten Geschäftsprozess modelliert:

Prozess 1: Vereinfachter Prozess zur Antragsverteilung

Dieser Prozess hat mit der Nachricht begonnen, dass ein neuer Antrag erstellt worden ist, ermittelte daraufhin die verfügbaren Berater, sortierte diese in ein Ranking, wählte den am höchsten gerankten Berater aus, wies diesen dem Antrag zu und kommunizierte dieses Ergebnis als Prozessende. Die Nachricht am Prozessende war ungerichtet und an keinen bestimmten Empfänger gerichtet. Es konnte einen Empfänger für die Nachricht geben oder auch nicht. Für den Geschäftsprozess hatte das keine Bedeutung.

In einer auf Ereignissen basierenden Microservice-Architektur ist diese Form der losen Koppelung häufig gewünscht. Die Grundidee dabei ist, dass bei ungerichteten Nachrichten die einzelnen Microservices voneinander unabhängiger sein können – denn schließlich müssen die Services nichts voneinander wissen. Doch was, wenn die Verarbeitung durch einen anderen Service einen Teil des modellierten Geschäftsprozesses darstellt? Was können wir tun, wenn aus fachlicher Sicht z. B. ein Antrag erst dann wirklich einem Berater zugeordnet ist, wenn dieser einen damit zusammenhängenden Arbeitsauftrag erhalten hat und diese Arbeitsaufträge von einem anderen Service verwaltet werden?

Choreographie oder Orchestrierung?

Auf Architektur-Ebene berührt das die Frage, ob Microservices orchestriert oder choreographiert werden sollten. Ein häufiger Ansatz bei Microservice-Architekturen ist der der Choreographie: Jeder Service publiziert dabei ein oder mehrere fachliche Ereignisse. Andere Services, die auf diese Ereignisse reagieren müssen, abonnieren die Ereignisse und verarbeiten sie. Der Geschäftsprozess, den wir bisher modelliert haben, entspricht diesem Ansatz: Es bleibt letztlich offen, ob ein anderer Service auf das versendete Ereignis reagiert oder nicht. Mit einer solchen Architektur benötigt man keine steuernde Instanz, keinen „Dirigenten“: Bildlich gesprochen „tanzen“ die Services von sich aus in einer Choreographie. Technisch sind die Microservices dadurch entkoppelt, doch eine mögliche fachliche Abhängigkeit ist nicht mehr direkt sichtbar.

Eine Alternative zur Choreographie stellt die Orchestrierung von Microservices dar. Bei der Orchestrierung übernimmt eine Instanz die Koordination der Microservices und „dirigiert“ sie. Auf diese Weise können übergreifende Geschäftsprozesse explizit abgebildet und nachvollzogen werden. Dabei ist darauf zu achten, dass keine starke Koppelung zwischen den Services entstehen darf und keine zentrale Instanz die fachliche und technische Unabhängigkeit der Microservices einschränkt. Eine Orchestrierung mit schwacher Koppelung ist auf Architektur-Ebene mit und ohne BPMN möglich. Netflix Conductor orchestriert Microservices zum Beispiel ohne BPMN und mit einer JSON DSL. Camunda Zeebee wiederum ermöglicht eine Orchestrierung mit BPMN. Diesen Lösungen gemein ist, dass die fachlichen Abhängigkeiten von Microservices nachvollziehbar und überwachbar sind. Doch auch ein Microservice allein kann ohne zentrale Instanz andere Services orchestrieren, wenn es für dessen Fachlichkeit sinnvoll ist – der Microservice selbst wird dann sinnbildlich zum Dirigenten eines kleinen Service-Orchesters. Wie könnte das in unserem Beispiel und in BPMN aussehen?

Ein Geschäftsprozess, zwei Services, ein Verantwortlicher

In einer Microservice-Architektur gibt es in der Regel viele Microservices mit jeweils eigenen Verantwortlichkeiten. Wie im vorherigen Teil dieser Artikelserie besprochen wird auch der bisher modellierte Geschäftsprozess zur Zuweisung von Anträgen in einem Microservice implementiert. Neben diesem Zuweisung-Service kann es auch andere Services geben, zum Beispiel einen Service für Arbeitsaufträge. Beide Services haben eigenständige, abgegrenzte Domänen.

Wenn der Geschäftsprozess für Zuweisungen nun ebenfalls das Erstellen eines bestimmten Arbeitsauftrages umfassen soll, dann bietet sich eine Orchestrierung der dahinterstehenden Services an. Erweitern wir den Prozess entsprechend um eine Aktivität „Arbeitsauftrag erstellen“:

Prozess 2: Antragsverteilung mit Arbeitsaufträgen

Der Service für Arbeitsaufträge wird dabei über das Versenden einer an den Service gerichteten Nachricht (einem „Command“) in den Geschäftsprozess eingebunden. Durch das Versenden und Empfangen von Nachrichten über einen Broker wie z. B. Apache Kafka werden synchrone Service-Aufrufe und eine enge technische Koppelung zwischen den Services vermieden. Nachrichten können sowohl verschickt als auch empfangen werden. In BPMN wird das Versenden einer Nachricht durch einen schwarzen Briefumschlag modelliert, das Empfangen durch einen weißen. Beides kann sowohl in der BPMN-Notation eines Ereignisses als auch als Aktivität modelliert werden.

Die Notation als Zwischenzustand (oberes Bild) ist sinnvoll, wenn der Geschäftsprozess selbst einen Zustand erreicht und diesen als Ereignis über eine Nachricht publiziert. Die Modellierung als Aktivität ist allgemein sinnvoll, wenn es sich um eine gezielte Nachricht an einen anderen Service handelt. Der zweite Service ist nun fachlich in den Geschäftsprozess eingebunden, technisch aber nach wie vor nur lose gekoppelt.

Doch was passiert, wenn der Service für Arbeitsaufträge nicht reagiert, die Nachricht nicht ankommt oder es einen anderen Fehler in der Verarbeitung gibt? Bisher kann der Geschäftsprozess auf diese Art von Fehlern nicht reagieren.

Das Saga-Pattern in BPMN

Da mit Microservice-Architekturen eine enge Koppelung der Services vermieden werden soll, verfügt jeder Microservice in der Regel über eine eigene Persistenzschicht. Bei Fehlern in übergreifenden fachlichen Prozessen kann daher nicht einfach eine Datenbank-Transaktion zurückgerollt werden, um auf Fehler zu reagieren und einen konsistenten Zustand wiederherzustellen. Das Saga-Pattern beschreibt mit Kompensationen eine Lösung für diese Herausforderung. Kompensationen sind Aktivitäten, die im Fehlerfall die Auswirkungen von zuvor ausgeführten Aktivitäten rückgängig machen. Für eine Aktivität „Arbeitsauftrag erstellen“ wäre die mögliche Kompensation zum Beispiel „Arbeitsauftrag löschen“. Kompensationen lassen sich direkt in BPMN durch ein Zurückspulen-Symbol modellieren:

Im normalen Prozessablauf werden die Kompensationen nicht ausgeführt. Sie können in BPMN aber zum Beispiel durch einen speziellen Endzustand ausgelöst werden:

Wird dieser Endzustand erreicht, werden automatisch alle Kompensationen ausgeführt, die zu Aktivitäten gehören, die bis zum Erreichen des Endzustands ausgeführt worden sind. Die Ausführung erfolgt dabei in umgekehrter Reihenfolge.

Verteilte Services, zentrale Verantwortlichkeit

Mit dem Saga-Pattern und den Bausteinen „Kompositionen“ und „Nachrichten“ kann der Geschäftsprozess für die Zuweisung von Anträgen also zentral im Zuweisung-Service implementiert werden, andere Services können orchestriert werden und auf Fehler kann reagiert werden:

Prozess 3: Antragszuweisung mit Saga-Pattern

Der überarbeitete Prozess beginnt damit, dass ein neuer Antrag erstellt worden ist. Im Anschluss werden die verfügbaren Berater ermittelt, ein Ranking erfolgt und der am besten gerankte Berater wird zugewiesen. Diese Zuweisung wird als Ereignis per Nachricht ungerichtet kommuniziert. Damit der Berater, an den der Antrag zugewiesen wurde, auch einen Hinweis und einen Arbeitsauftrag erhält, wird der Service für Arbeitsaufträge mit einer an ihn gerichteten Nachricht aufgefordert, einen solchen Auftrag anzulegen. Daraufhin erwartet der Zuweisung-Service eine Nachricht, dass der Auftrag erfolgreich angelegt worden ist. Auf diese Antwort wartet der Service mit einem modellierten Timeout. In einer Workflow-Engine wie Camunda wird durch das Warten kein Thread blockiert. Erfolgt die erwartete Rückmeldung nicht, dann werden die Kompensationen ausgelöst. Grundsätzlich kann der Prozess auch so modelliert werden, dass die Kompensationen bei beliebigen Fehlern ausgelöst werden. Die Workflow-Engine stellt dabei sicher, dass nur Kompensationen ausgeführt werden, die zuvor ausgeführte Aktivitäten zurücknehmen.

Als Kompensation für die Nachricht an den Service für Arbeitsaufträge wird in unserem modellierten Prozess eine neue gerichtete Nachricht an den Arbeitsauftrag-Service geschickt. Mit dieser Nachricht soll ein eventuell dennoch angelegter Arbeitsauftrag gelöscht werden. Im Anschluss wird auch die zuvor erfolgte Zuweisung durch eine Kompensation zurückgenommen.

Der Geschäftsprozess beschreibt damit die Zuweisung von neuen Anträgen an Berater und endet erst dann, wenn die Berater einen entsprechenden Arbeitsauftrag erhalten haben oder es zu einem Fehler kommt. Mit Hilfe des Saga-Patterns sind die Microservices nach wie vor technisch nur lose gekoppelt, obwohl der gesamte Ablauf des Geschäftsprozesses an einer Stelle modelliert und implementiert ist.

Schreibe einen Kommentar