Interview mit Guido Schmutz

Microservices: Warum CQRS & Event Sourcing auch zusammenpassen, CRUD nicht veraltet ist und wie Apache Kafka helfen kann

Dominik Mohilo

Guido Schmutz

Sind CQRS und Event Sourcing wirklich wie Feuer und Wasser, oder lassen sich die Muster vielleicht doch im Microservices irgendwie kombinieren? Wir sprachen mit Guido Schmutz, Solution Architect bei Trivadis, über diese und weitere Fragen. Im Interview zur W-JAX 2019 erklärt er zudem, was ein Event Store macht, warum CRUD nicht wirklich veraltet ist und wie man Apache Kafka im Microservices-Kontext einsetzen kann.

JAXenter: Hallo Guido und danke, dass du dir die Zeit genommen hast. In deinem Talk auf der W-JAX 2019 geht es um Apache Kafka als Event Store. Daher zunächst einmal die Frage, was genau macht ein Event Store?

Guido Schmutz: Ein Event Store übernimmt bei der Umsetzung des Event Sourcing Patterns die Speicherung der Ereignisse und bietet eine Möglichkeit, dies in der richtigen Reihenfolge wieder abzuspielen, um jederzeit den aktuellen Zustand eines Objekts bzw. einer Entität zu rekonstruieren. D.h. ein Event Store ist eine Datenbank, die bestimmte Eigenschaften anbieten muss und die Umsetzung von Event Sourcing möglich macht.

JAXenter: Apache Kafka ist ja eher aus dem Bereich Big Data und Streaming bekannt. Wie kann es in Sachen Microservices behilflich sein?

Guido Schmutz: Ja, das ist richtig, Apache Kafka ist hauptsächlich aus dem Bereich Big Data und Streaming bekannt. So bin auch ich vor 6 Jahren auch auf Kafka gestoßen, als wir die damals beliebte Lambda-Architektur umgesetzt haben, mit einem Batch- und Speed-Layer und Kafka als Weiche, um die Daten sicher und effizient an die entsprechenden Konsumenten im Batch- bzw. Speed-Layer weiterzuleiten.

Bei LinkedIn (dem Unternehmen hinter Apache Kafka) ist Kafka jedoch eher für die Entkoppelung der Applikationen entstanden, d.h. LinkedIn hat sein System von Beginn an nicht als Monolith gebaut, sondern in Form kleiner, unabhängiger Applikationen, die man heute als Microservices bezeichnen würde.

Mit Apache Kafka gibt es Infrastrukturen , die das Publish/Subscribe Pattern unternehmensweit auf zuverlässige und skalierbare Weise unterstützen können.

Die Frage, die sich in einer Microservices-Architektur stellt, ist, wie die Microservices untereinander kommunizieren sollen. Geht das nur über REST APIs, d.h. vorwiegend synchron, oder soll dies auch asynchron erfolgen? Ein synchroner Ansatz eignet sich dann, wenn ich als Aufrufer vom Microservice eine Antwort brauche bzw. einfordere. Dies hat aber den großen Nachteil, dass man damit die Services viel enger koppelt, als man möglicherweise möchte. Ein wichtiger Aspekt von Microservices ist ja gerade die Entkoppelung, und dies sollte nicht nur zur Entwicklungszeit, sondern eben auch zur Laufzeit erfüllt sein. Wenn es bei der Interaktion primär darum geht, einen anderen Microservice zu informieren, dann ist eine ereignisgetriebene Interaktion wesentlich besser. In diesem Fall publiziert der Microservice wichtige geschäftsrelevante Änderungen nicht über synchrone Schnittstellen sondern asynchron über das Publizieren eines Events an einen Message Broker bzw. Event Hub. Jeder Microservice, der sich nun für das Ereignis interessiert, kann eine Subskription auf dem Event erstellen und die Events konsumieren. Dies ist vom Prinzip her nichts neues und als Publish/Subscribe Pattern bekannt. Neu ist, dass es mit Apache Kafka Infrastrukturen gibt, die das Publish/Subscribe Pattern unternehmensweit auf zuverlässige und skalierbare Weise unterstützen können und auch bei sehr hohen Nachrichtenvolumina nicht „schlapp“ machen.

Dadurch, dass sich alle beteiligten Microservices über eine zentrale Infrastruktur, sprich einen Apache Kafka Cluster interagieren, kann auch eine stattliche Anzahl von Nachrichten pro Zeiteinheit entstehen, was für Kafka aber wie gesagt kein Problem darstellt. Noch wichtiger für Microservices wird jedoch sein, dass Kafka die Nachrichten nicht sofort löscht und man sehr fein konfigurieren kann, wann Kafka Nachrichten entfernt. Damit lassen sich Nachrichten auch mehrfach konsumieren, sei dies weil beim ersten Konsumieren ein Fehler unterlaufen ist oder aber weil ein neuer Microservice beim Bootstrapping an Ereignissen interessiert ist, die früher publiziert wurden.

Wird Kafka in der Architektur so positioniert, dass zukünftig der größte Anteil an Interkationen darüber geschieht, d.h. nicht nur Microservices, sondern auch externe, native Datenströme daran teilhaben, dann kann sich auch ein Data Lake als Konsument bedienen, um Daten in großer Menge und historisiert für komplexe Analysen (z.B. Machine Learning/AI) zur Verfügung zu stellen. Zudem stehen die Ereignisse in Kafka auch für Echtzeit-Analysen zur Verfügung, dem sogenannten Stream Analytics. Hier bietet das Apache-Kafka-Projekt neben dem Broker mit Kafka Streams eine Java Library zur Verfügung, mit welcher jede Java-Applikation mit Stream-Analytics-Funktionen ergänzt werden kann. D.h. in einer Microservices-Architektur ist der Einsatz von Stream-Analytics-Mitteln lediglich die Technologieentscheidung eines einzelnen Microservice.

API Experten Dossier 2019

SIND SIE BEREIT FÜR DIE API-REVOLUTION?

In über 30 Seiten beleuchten Experten APIs aus allen Blickwinkeln und teilen ihre Erfahrung, Ideen und Lösungsansätze mit Ihnen.
Sichern Sie sich jetzt kostenlos sieben Fachbeiträge und Interviews zu spannenden Themen wie dem Design, der Entwicklung und der Systemintegration von APIs.

 

JAXenter: Welchen Unterschied gibt es zwischen den beiden Ansätzen „Event Sourcing“ und „CQRS“? Oder lassen sich die beiden Ansätze am Ende gar kombinieren?

Guido Schmutz: In traditionellen, monolithischen Systemen werden sowohl schreibende Operationen (Änderungen an den Entitäten) wie auch schreibende Operationen (Anzeigen von Entitäten) gegen das gleiche Set von Objekten in einer Datenbank ausgeführt. Oft entsprechen die Entitäten einzelnen Zeilen in einer oder mehreren Tabellen in einer relationalen Datenbank wie MySQL oder Oracle. Typischerweise werden in diesen Systemen alle CRUD (Create, Read, Update und Delete) Operationen gegen dieselbe physische Repräsentation der Entitäten angewandt. Dieser Ansatz für die Datennutzung ist relativ einfach zu implementieren und hat sich über die Jahre mit Unterstützung durch entsprechende Frameworks durchgesetzt.

Beim CQRS Pattern werden die lesenden Operationen klar von den schreibenden Operationen getrennt und über separierte Interfaces abgehandelt. Dies impliziert, dass die Datenmodelle, die für die Abfrage der Entitäten und für die Änderungen an den Entitäten genutzt werden, auch unterschiedlich sein können. Man spricht dabei vom Write Model, das schreibende Operationen unterstützt, und vom Read Model, welches die lesenden Operationen unterstützt. Entscheidet man sich für eine Trennung der Datenmodelle, dann können diese über unterschiedliche Schemata, unterschiedliche Datenbankinstanzen oder sogar unterschiedliche Persistenztechnologien aufgeteilt werden. In jedem Fall muss eine Replizierung der Daten vom Write Model zum Read Model adressiert werden. Wenn das Write Model und das Read Model getrennt werden, dann wird es typischerweise auch viel einfacher, den lesenden Anteil an Operationen über mehrere Replikate zu skalieren, da es sich nun nur noch um Kopien bzw. Sichten auf das Write Model handelt.

Wenn das Write- und das Read-Modell getrennt werden, wird es typischerweise auch viel einfacher, den lesenden Anteil an Operationen über mehrere Replikate zu skalieren.

Bei Event Sourcing wird nicht der Zustand der Entitäten gespeichert, sondern die einzelnen Ereignisse, die zu diesem Zustand geführt haben. Das Hello-World-Besipiel dazu ist das Führen eines (Bank-)Kontos. D.h. bei Event Sourcing speichert man nicht mehr den aktuellen Saldo des Konto (z.B. EUR 500), sondern die einzelnen Transaktionen, die zu diesem Saldo geführt haben (z.B. Kontoeröffnung mit EUR 100, Einzahlung von EUR 600, Bezug von EUR 200). Diese drei Ereignisse sind nicht mutierbar (immutable) und werden nur fortgeschrieben. Beim Event Sourcing dient der Event Store für die Speicherung bzw. das Fortschreiben dieser Ereignisse. Das Vorgehen bei Event Sourcing entspricht der klassischen, papierbasierten Buchführung. Will man beim Event Sourcing den aktuellen Zustand (d.h. den aktuellen Saldo) wissen, dann müssen die Ereignisse vom Event Store gelesen und in der richtigen Reihenfolge abgespielt werden. Dies ist sowohl bei Abfragen wichtig (meistens interessiert nur der aktuelle Saldo des Kontos) aber auch bei schreibenden Operationen (die Bank will nur Geld auszahlen, wenn der Saldo auch ausreichend ist). Das Abspielen der Events bei jeder schreibenden und lesenden Operation kann natürlich recht ineffizient werden, insbesondere wenn ein Objekt viele Ereignisse aufweist, was beim Beispiel vom Konto über die Jahre durchaus der Fall sein kann.

Genau hier kann eine Kombination von CQRS und Event Sourcing Abhilfe schaffen, indem Event Sourcing für das Write Model verwendet wird und die lesenden Zugriffe über Replikate erfolgt, die genau die Daten umfassen, die die lesende Operation braucht, d.h. etwa den aktuellen Zustand eines Objektes. Damit ist das Abspielen der Events nur noch dann notwendig, wenn eine schreibende Operation erfolgt. Ist auch dies nicht effizient genug, dann kann ein Event Store die Möglichkeit eines Snapshots anbieten, d.h. nach einer bestimmten Anzahl von Ereignissen wird der aktuelle Zustand als Snapshot eingefügt, der dann bei der nächsten schreibenden Transaktion als Aufsatzpunkt dient.

JAXenter: Welcher Ansatz ist für Microservices-Architekturen besser geeignet?

Guido Schmutz: Wie oft in der IT: „it depends“. Beide sind für Microservices-Architekturen geeignet. Aber natürlich auch weiterhin das traditionelle CRUD-Muster.

CQRS allein lässt sich interhalb eines Microservices (lokales CQRS) aber auch über Microservices hinweg (systemweites CQRS) nutzen. Im zweiten Fall kann sich ein Microservice von einem anderen unabhängiger machen, indem er die für ihn wichtigen Daten (die vom besitzenden Microservice über Ereignisse publiziert werden) bei sich als Read Model speichert. Dadurch muss er nicht mehr (in jedem Fall) auf die Daten des führenden Microservice über dessen Schnittstelle zugreifen, sondern kann sie aus dem lokalen Cache beziehen (Read Model). Damit lässt sich eine Laufzeitabhängigkeit zwischen den beiden Services eliminieren.

Für den lokalen Cache kann der Microservice die Persistenztechnologie wählen, die für das Problem am besten passt. Dies kann ein RDBMS, eine NoSQL-Datenbank oder selbst eine In-Memory-Datenbank sein. Das Read Model ist ja nur ein Replikat, welches durch den Einsatz eines Event Hubs für die Publish/Subscirbe-Infrastruktur wieder neu gebaut werden kann, da ein Event Hub auch alte, schon konsumierte Ereignisse aufbewahren kann. Hier kommt erneut Kafka ins Spiel, die zurzeit bekannteste Implementation des Event-Hub-Architekturbausteins. Bei Kafka kann sehr fein gesteuert werden, wie lange einzelne Ereignisse im Event Hub bleiben sollen, d.h. es ist auch denkbar, dass man Kafka so konfiguriert, dass sich jederzeit neue Microservices aus der Historie von allen Events bedienen können, um die lokal benötigten Caches bzw. Read-Modelle aufzubauen.

Event Sourcing hingegen ist nur lokal innerhalb eines Microservices zu sehen. D.h. der Microservice kann sich entscheiden, ob er weiterhin nur den aktuellen Zustand oder aber vielmehr die einzelnen Ereignisse speichern, d.h. Event Sourcing nutzen will. Die Nutzung von Event Sourcing wird dann oft mit lokalem oder auch systemweitem CQRS kombiniert, um die Effizienz von lesenden Operationen zu steigern.

JAXenter: Was ist die Kernbotschaft aus deiner Session, die jeder mit nach Hause nehmen sollte?

Die Broker-Funktionalität von Apache Kafka allein eignet sich nur schlecht als Event Store.

Guido Schmutz: Event Sourcing und CQRS sind beides interessante Pattern, welche zusätzliche Möglichkeiten bieten. CQRS lässt sich unabhängig von Event Sourcing nutzen, Event Sourcing ohne CQRS macht eher weniger Sinn. Der Event Store spielt eine wichtige Rolle bei der Umsetzung von Event Sourcing und sollte die verschiedenen Aspekte von Event Sourcing entsprechend auch unterstützen. Die Broker-Funktionalität von Apache Kafka allein eignet sich nur schlecht als Event Store. Datenbanken wie RDBMS oder NoSQL haben hier Vorteile und es gibt entsprechende Frameworks, die die Umsetzung eines Event Stores auf bestehenden Datenbanken unterstützten. Kafka kann dabei die Rolle der zuverlässigen Publish/Subscribe-Infrastruktur übernehmen.

Wird Apache Kafka zusammen mit Kafka Streams genutzt, dann lassen sich wesentliche Aspekte von Event Sourcing erreichen, indem der aktuelle Snapshot im Zustand des Stream-Prozessors gehalten wird. CQRS ohne Event Sourcing kann mit Apache Kafka gerade auch systemweit sehr gut unterstützt werden. Beide Pattern führen jedoch zu zusätzlicher Komplexität und man sollte sie nur dann nutzen, wenn es sinnvoll ist.

JAXenter: Vielen Dank für das Interview!

Guido Schmutz arbeitet als Solution Architect für das IT-Dienstleistungsunternehmen Trivadis. Er ist seit über dreißig Jahren als Softwareentwickler, Berater, Architekt, Trainer und Coach tätig. Bei der Trivadis verantwortet er die Bereiche SOA, BPM und Applikation Integration und ist Leiter des Trivadis Architecture Board. Sein Interesse liegt in der Architektur, dem Design und er Umsetzung von modernen Softwarelösungen. Seine Spezialgebiete sind Java EE, Spring, Oracle SOA Suite und Oracle Service Bus. Seit einigen Jahren beschäftigt er sich hauptsächlich mit NoSQL, Big Data & Fast Data und der Frage, wie sich diese neuen Technologien optimal in eine moderne Software Architektur integrieren lassen. Er ist regelmäßiger Sprecher auf internationalen Konferenzen und ist bekannt als Autor von Fachartikel und diversen Fachbücher. Guido ist Groundbreaker Ambassador und ACE Director.
Geschrieben von
Dominik Mohilo
Dominik Mohilo
Dominik Mohilo studierte Germanistik und Soziologie an der Goethe-Universität in Frankfurt. Seit 2015 ist er Redakteur bei S&S-Media.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
4000
  Subscribe  
Benachrichtige mich zu: