Transcript

Event Sourcing und CQRS – Teil 1

Vor- und Nachteile sowie Einsatz in verteilten Anwendungen

In dieser Folge unterhalten sich Niko Will und Lucas Dohmen über Event Sourcing und Command Query Responsibility Segregation, kurz CQRS. Wozu kann man diese Patterns verwenden? Was sind Vor- und Nachteile gegenüber anderen Ansätzen? Warum sind sie besonders in verteilten Anwendungen wie in Microservice Architekturen oder Self-Contained Systems (SCS) interessant?

Back to episode

Transcript

Niko Will: Ja, hallo!

Lucas Dohmen: Wer bist du denn? Stell dich doch mal kurz vor.

Niko Will: Ja, mein Name ist Niko Will. Ich bin Consultant bei innoQ, bin 31 Jahre alt und beschäftige mich vor allem mit verteilten Softwarearchitekturen.

Lucas Dohmen: Das passt dann ja ganz gut zu dem Thema heute. Vielleicht steigen wir dann direkt mal mit der Frage ein: Was ist denn überhaupt Event Sourcing und was macht das anders, als man das konventionell kennt von Anwendungen?

Niko Will: Genau. Also, Event Sourcing selber ist erst mal ein internes Pattern, das eigentlich nur beschreibt, wie man Zustände persistiert. Konventionell ist es ja so, dass ich das nach dem typischen CRUD-Prinzip mache, also Create, Read, Update und Delete auf eine Datenbank. Da ist es in der Regel so: Wenn ich eine Zustandsänderung habe, dann überschreibe ich quasi den letzten Zustand, der in der Datenbank steht. Beim Event Sourcing hingegen macht man das so, dass die Zustände einer Domäne sich aus der Summe der einzelnen Events, die über die Zeit eingetreten sind, gebildet werden. Deswegen kann aber die API der Anwendung selber trotzdem CRUD sein. Also, ich kann trotzdem etwas anlegen, etwas updaten und etwas löschen, sodass ein Update in der Persistenz selber nicht zu einem Update führt, sondern einfach zu einem weiteren Insert, einem weiteren Event.

Lucas Dohmen: Also das klingt für mich ja jetzt erst mal wesentlich komplizierter als der konventionelle Ansatz. Welche Probleme siehst du denn bei dem konventionellen Ansatz, also warum wollen wir denn überhaupt einen neuen Ansatz dafür haben?

Niko Will: Ja, also prinzipiell ist es beim konventionellen Ansatz so, dass er einfach im Vergleich zu Event Sourcing jetzt eher eine schlechtere Performance und auch eine Responsiveness der Anwendung und auch einfach eine schlechtere Skalierbarkeit der Anwendung hat. Dazu muss man einfach wissen, wenn man sich überlegt, wie das im konventionellen Ansatz gemacht wird: Ich mache typischerweise Updates. Update bedeutet, ich muss in der Datenbank erst mal den jetzigen Eintrag suchen, ich muss ihn blockieren, also ich muss ihn zum Schreiben quasi locken, dann kann ich ihn erst Überschreiben und hinterher muss ich ihn wieder freigeben, dass andere auch Updates drauf ausführen können. Das ist einfach, wenn ich eine Domäne habe, wo ich sehr viele gleichzeitige schreibende Zugriffe vor allem auf einen speziellen Datensatz, zum Beispiel, habe, kann es natürlich zu Konflikten führen, bzw. Konflikte sind hier sehr viel wahrscheinlicher. Zusätzlich ein weiterer Nachteil an der typischen CRUD- Geschichte ist, dass ich keine Historie hab. Also ich kann nie nachvollziehen, wie der aktuelle Zustand, den ich in der Datenbank habe, überhaupt zustande gekommen ist.

Lucas Dohmen: Und was ist da beim Event Sourcing anders und wie löst es diese Probleme?

Niko Will: Genau, beim Event Sourcing ist es so… Ein Event ist erst mal ein immutable Fact, also das ist etwas, was in der Vergangenheit aufgetreten ist, was sich aber auch nicht mehr ändert. Das bedeutet, Events werden nur geschrieben, sie werden nie verändert. Das hat einfach den großen Vorteil, dass ich die in meiner Persistenz immer nur hinten anfügen muss. Dadurch sind einfach schreibende Zugriffe sehr viel schneller möglich, als es eben in dem typischen CRUD-Szenario der Fall ist.

Lucas Dohmen: Weil ich dann nicht mehr ein Lock machen muss, wenn ich ein Update machen will?

Niko Will: Genau.

Lucas Dohmen: Verstehe. Um das jetzt mal ein bisschen konkreter zu machen – kannst du mal einen Ablauf schildern, in einem typischen System, wie der mit Event Sourcing funktionieren würde?

Niko Will: Klar, natürlich. Also ein typisches Beispiel: Sagen wir mal, ein Benutzer registriert sich bei einer Warenwirtschaft oder möchte auf Amazon bestellen. Das heißt, der Benutzer registriert sich bei Amazon, gibt seine Daten ein, klickt auf “Anlegen”. Im Hintergrund in der Anwendung wird aus den kompletten Daten ein Event erzeugt, zum Beispiel ein “UserRegistered” Event. Das Augenmerk liegt hier, dass sie immer in der Vergangenheit definiert sind, also User registered. Wenn er jetzt später sich dann nochmal auf der Webseite einloggt, und ändert zum Beispiel seine Adresse, dann wird aus dieser Adressänderung wird ein “AddressChanged” Event, das in der Payload selber lediglich die neue Adresse hat. Wenn ich jetzt einfach diese zwei Events nacheinander wieder einspiele, dann habe ich zuerst, dass der User sich angelegt hat, dann wurde die Adresse geändert, und damit habe ich den aktuellen Zustand. Sein komplettes Profil, inklusive der neuen Adresse, die die alte Adresse im State überschreibt.

Lucas Dohmen: Aber die alte Adresse ist im alten Event immer noch vorhanden.

Niko Will: Im alten Event ist sie weiterhin vorhanden, genau. Wir fügen nur neue Events an, wir ändern nichts in irgendwelchen alten Events.

Lucas Dohmen: Okay, und wie muss ich mir das dann vorstellen, wie das gespeichert wird? Also, wie sichere ich so ein Event in meinem System?

Niko Will: Ja, also die Events werden in einen sogenannten Event Store geschrieben. Events sollten – oder müssen eigentlich – pro Aggregate in Reihenfolge gespeichert werden. Das heißt eine Aggregate-ID, Sequence Number und eine Payload. Die Sequence Number ist einfach fortlaufend, quasi. Ein Aggregate ist hier ein Begriff aus dem Domain-driven Design, und bezeichnet eine zusammengehörige Einheit. Das bedeutet einfach nur, dass sich Änderungen nie über mehrere Aggregates erstrecken sollten, da sonst Transaktionen notwendig werden. Sprich, wenn ich einfach zwei Aggregates habe, dann bräuchte ich ja auch zwei Events. Jetzt kann natürlich der Fall auftreten, ich ändere das erste Aggregate, was ein Event erzeugt. Das wird in den Event Store geschrieben. Jetzt möchte ich dieses zweite Aggregate auch ändern. In dem Moment stirbt die Anwendung, stürzt ab. Dann wurde das zweite Event noch nicht geschrieben. Jetzt habe ich einen inkonsistenten Zustand. Deswegen muss man immer darauf achten, dass sich Events immer nur auf ein Aggregate beziehen.

Lucas Dohmen: Und wie muss ich mir das vorstellen? Diesen Event Store, ist das eine spezielle Software? Was für Anforderungen habe ich, wenn ich so einen Event Store haben möchte?

Niko Will: Ja also, prinzipiell kann eigentlich fast jede Datenbank als Event Store verwendet werden. Obwohl sich da natürlich einige besser eignen als andere. So ein typisches Beispiel wäre hier zum Beispiel Cassandra, einfach weil Cassandra ein Multi-Master System ist und damit Inserts auf jedem Knoten erlaubt. Aber prinzipiell kann ich da dafür auch 'ne Postgres zum Beispiel nehmen. Wichtig ist aber, dass der Event Store die Reihenfolge garantieren kann. Also sprich, die Events werden ja mit Sequenznummer versehen, und ich muss dann quasi die Events nach Sequenznummer sortieren können, wenn ich sie abfrage. Darüber hinaus ist es sehr interessant wenn der Event Store das ermöglicht, dass ich Events selektiv aus dem Event Store auslesen kann. Das bedeutet, ich muss ja – also potenziell werden ja sehr viele Events von verschiedenen Aggregates gespeichert. Und um ein Aggregate wiederherstellen zu können, muss ich alle Events von diesem einen Aggregate lesen. Da ist einfach wichtig, dass ich quasi “by Id” abfragen kann, also “gib mir alle Events in order für eine bestimmte Id”. Das ist einfach ein sehr großer Vorteil, weil sonst müsste ich potenziell viele Events lesen, die mit dem eigentlichen Aggregate, dessen Zustand ich wiederherstellen möchte, eigentlich gar nichts zu tun haben.

Lucas Dohmen: Du hast jetzt erklärt, wie ich dafür sorge, dass ich zu einem bestimmten Aggregate alle Events speichern kann. Aber wie komme ich jetzt zu dem State? Also, ich hab jetzt irgendwie eine ganze Liste von Events, die nacheinander passiert sind, zu einem Aggregate, wie komme ich jetzt da hin, dass ich jetzt zum Beispiel einen User abfragen kann.

Niko Will: Okay. Also prinzipiell, mithilfe dieser Events, die wir gespeichert haben, lässt sich der aktuelle Zustand eines Aggregates jederzeit wiederherstellen. Folglich kann man aber eine Anwendung auf zwei Arten implementieren. Einerseits, wenn ich auf den User zum Beispiel zugreifen möchte, dann wird bei jedem Zugriff auf diesen User, werden jedes Mal alle Events für diesen User erneut eingelesen, um den Zustand wiederherzustellen. Das kann für kleinere Anwendungen, wo es insgesamt vielleicht sehr wenige Events je Aggregate gibt oder wo nicht sehr oft auf den Zustand – also wo ich den einfach nicht so oft wiederherstellen muss – kann das funktionieren. In der Regel ist es aber so, dass man dann guckt, dass der Zustand im Speicher gehalten wird. Und die Events im Event Store dienen dann wirklich nur dazu, wenn jetzt ein Knoten crashen sollte, dass ich aus dem Event Store den Zustand wiederherstellen kann, im Speicher.

Lucas Dohmen: Wenn ich mir das jetzt so anhöre, klingt das ja so – diese ganzen Events, es können ja durchaus sehr viele pro Aggregate werden, und das könnte ja durchaus länger dauern die hintereinander anzuwenden. Was gibt es denn da für Möglichkeiten?

Niko Will: Ja so prinzipiell kann man an der Stelle natürlich immer auch Snapshots speichern. Dann habe einfach den Zustand nochmal komplett irgendwo und muss dann nicht alle Events wieder einspielen sondern kann nur die Events die neuer sind als der spezifische Snapshot dann nochmal wieder einspielen. Wichtig ist aber an der Stelle: Auch wenn man Snapshots speichert, die alten Events auf keinen Fall löschen. Weil selbst das selektive Löschen ist eine sehr langsame Operation und blockiert einfach in dem Moment die Datenbank. Wenn ich das an vielen Stellen mache, dann wird meine Anwendung doch wieder langsam. Und außerdem sehe ich es einfach so, dass auch in diesen Events extrem wertvolle Informationen stecken, die ich einfach nicht verlieren möchte.

Lucas Dohmen: Ok, verstehe. Das bedeutet aber, wenn ich – du hattest ja eben diese zwei Ansätze erklärt – wenn ich den zweiten Ansatz wähle und den State separat halte, dann habe ich jetzt eine stateful Anwendung. Also, wenn ich jetzt irgendwie so an diese Twelve-Factor App denke, da steht ja drin “kein State in der Applikation”, jetzt habe ich State in meiner Applikation, richtig?

Niko Will: Genau. Aber aus meiner Sicht ist stateless eh ein Mythos. Also irgendjemand hat immer einen State, sonst hat man halt die Datenbank. Klar, das hat natürlich auch die Probleme, wenn mein State nicht mehr auf einen Knoten passt, dann muss ich eventuell mich ums Clustering kümmern, dass ich mehrere Knoten hab, aber da gibt es zum Beispiel auch die bekannten Event Sourcing Frameworks, die einen dabei dann auch unterstützen können. In meinem konkreten Fall, ich mache ziemlich viel mit Akka, oder auch mit dem darauf aufsetzendem Lagom. Die können einem da dann helfen und einfach so ein bisschen die pain points abnehmen.

Lucas Dohmen: Du hattest am Anfang gesagt, dass die Events unveränderlich oder immutable sind. Was für Konsequenzen hat das denn?

Niko Will: Genau, also ich finde sehr positiv an diesen Eigenschaften ist einfach – ich habe quasi automatisch einen Audit Log. Also welche Änderungen im System wann von wem durchgeführt wurden und so weiter. Das habe ich einfach schon mal out-of-the-Box, bekomme ich das geliefert. Was für manche Anwendungen alleine schon ein Grund ist, Event Sourcing zu verwenden. Was ich persönlich sehr super finde, dass ich bei Fehlern in der Produktion, also ich hab ein System in der Produktion, da tritt ein Bug auf. Da passiert irgendwas nicht vorhergesehenes. Dann kann ich einfach hergehen, ich schnappe mir die kompletten Daten aus der Produktion, spiele die in ein Testsystem ein und kann in dem Testsystem debuggen. Weil in dem Testsystem kann ich den aktuellen Zustand wiederherstellen, indem ich alle Events einspiele, und kann da auch Inkonsistenzen einfach feststellen, die ich jetzt mit einer CRUD Datenbank dahinter so nicht feststellen kann. Weil da habe ich nur den aktuellen Zustand. Der ist vielleicht jetzt in dem Moment inkonsistent, aber was dazu geführt hat, dass der inkonsistent wurde, das kann ich im Nachhinein nicht mehr feststellen. Mit den Events in dem Testsystem kann ich das debuggen und kann wirklich feststellen, welche Änderung in meinem System hat jetzt zu diesem inkonsistenten Zustand geführt. Und – wenn jetzt Bugs behoben wurden, dann kann ich hinterher im Produktivsystem einfach die Events nochmal von vorne einspielen. Und habe damit diesen inkonsistenten Zustand eliminiert. Das ist wirklich sehr praktisch. Und zusätzlich ist es einfach so, ein weiterer Vorteil von Event Sourcing, den ich sehe, ist, dass in diesen Events einfach sehr wertvolle Informationen für zum Beispiel Business Analytics stecken. Zum Beispiel, wann hat welcher User viel eingekauft. Das sind Informationen, die ich in einer CRUD-Datenbank so nicht mehr habe.

Andererseits gibt natürlich auch negative Aspekte. Potenziell negativ ist natürlich ein erhöhter Speicherbedarf. Obwohl wir – wie ich ja gerade gesagt habe, ich speichere zwar mehr, weil ich alle Events speichere. Aber dadurch habe ich ja auch potenziell mehr Informationen, auch wenn ich die jetzt vielleicht noch nicht sofort nutzen mag, kann ich die später vielleicht noch in wertvolle Informationen, die mir ein Business Value bescheren, umwandeln. Und damit ist für mich der erhöhte Speicherbedarf eigentlich wieder ein bisschen relativiert.

Ein etwas krasseres Beispiel sind personenbezogene Daten. Also gerade in Deutschland ist es so, dass es natürlich Anwendungsfälle gibt, wo ich personenbezogene Daten speichere, und zum Beispiel auf Wunsch des Kunden oder wegen gesetzlichen Regelungen auch tatsächlich aus der Datenbank löschen muss. In dem Fall muss ich natürlich auch tatsächlich die Events löschen. Das ist jetzt aber wirklich einer der wenigen Fälle, wo ich Events löschen würde. Ein anderes Problem ein bisschen beim Event Sourcing, ist zum Beispiel Schema- Migration. Wenn ich jetzt das Datenbank-Schema, wie ich diese Events abspeichere, ändere, dann muss ich natürlich weiterhin alte Events auch noch einlesen können. Nicht nur nach dem neuen Format, sondern auch die, die nach dem alten Format gespeichert sind. Hier kann ich mir aber vielleicht auch ein bisschen mit Snapshots behelfen. Also sprich, ich führe ein neues Format ein, unterstütze das alte aber weiterhin. Lese alle alten Events ein, stelle den Zustand wieder her, speichere mir diesen Zustand als Snapshot ab, und in späteren Versionen der Software kann ich dann quasi das alte Eventformat ausbauen, weil ich immer ab diesem Snapshot quasi nur Events in dem neuen Format lese. Man sollte sich dann natürlich aber trotzdem die Eventspezifikation der alten Events noch merken, weil eventuell brauche ich die zum Beispiel zum Bugfixen und so weiter immer noch, beziehungsweise für Audit Logs.

Lucas Dohmen: Ok, also würdest du sagen, Events dürfen durchaus gelöscht werden, aber wirklich nur in diesem größten Ausnahmefall, richtig?

Niko Will: So würde ich das zusammenfassen, ja, genau.

Lucas Dohmen: Ich habe den Begriff Event Sourcing häufig vor allem im Zusammenhang mit Microservices gehört. Was vermutlich auch ein bisschen daran liegt, dass Microservices momentan immer noch ein Trendthema sind. Aber gibt es da noch andere Gründe? Spielen die gerade besonders gut zusammen, zum Beispiel?

Niko Will: Ja also, Events sind ja Tatsachen. Und Tatsachen kann ich erstmal ohne Kenntnis darüber, welches System tatsächlich an diesen Tatsachen interessiert ist, kann ich die veröffentlichen. Das unterscheidet es so ein bisschen vom Message-Driven-Ansatz, also wo ich quasi meinen Gesprächspartner kennen muss, ist es bei diesem Event-Driven-Ansatz so, dass ich quasi Events einfach veröffentlichen kann. Damit ist es natürlich schon mal ein sehr gutes Pattern für Microservices, da ich eine lose Kopplung erreichen kann. Dennoch ist Event Sourcing selber eigentlich erst mal ein internes Pattern. Ich verwende das intern für das Persistieren der Events. Aus meiner Sicht macht es dann einfach Sinn, dass man eher, anstatt alle Events zu veröffentlichen, mit einem Domain Expert zusammensetzt und überlegt, welche Events tatsächlich veröffentlicht werden sollen. Also in der Regel sind das dann sogenannte Domain Events, die einen Business Value haben. Da macht es einfach mehr Sinn, dann nur diese zu veröffentlichen, damit ich auch, je nachdem was ich verwende, welches Messaging System, nicht das Messaging System flute mit Events, die einfach für andere Systeme gar nicht interessant sind.

Lucas Dohmen: Einen anderen Begriff, den ich in dem Zusammenhang auch häufiger schon gehört habe, ist CQRS. Ist das, was du jetzt gerade beschrieben hast, schon CQRS? Oder ist das nochmal was anderes? Also irgendeinen Zusammenhang scheint es da ja zu geben, zwischen diesen Begriffen.

Niko Will: Ja, genau. Prinzipiell ist es so, bei CQRS – CQRS steht ja für Command Query Responsability Segregation – unterscheide ich im Prinzip zwei Arten von Operationen. Es gibt einmal die Queries – also die quasi den aktuellen Zustand vom System, oder von bestimmten Aggregates abfragen wollen, ohne sie zu ändern - und es gibt die sogenannten Commands. Ein Command hat quasi die Intention, den aktuellen Zustand zu ändern. CQRS bedeutet einfach nur, dass ich die beiden voneinander trenne in meiner Anwendung. Ich hab dann ein sogenanntes Write Model, das ist quasi die Command Seite, bei der geschrieben wird, bei der die Änderungen persistiert werden. Und ich hab ein, oder vielleicht auch mehrere sogenannte Read Models, also die quasi die Query Seite bedienen. Und ich teile es in die beiden Bereiche auf, und kann damit einfach [auf] die unterschiedlichen Anwendungen von schreibenden und lesenden Zugriffen eingehen. Was wir nämlich bisher bei Event Sourcing völlig außer Acht gelassen haben, ist, dass ich ja auch auf dem aktuellen Zustand irgendwie suchen möchte.

Lucas Dohmen: Okay. Und das würde ich jetzt bei CQRS lösen, indem ich das auf dem Read Model tue, richtig?

Niko Will: Genau. Und da erlaubt mir einfach, dass ich das Read Model auf die jeweiligen Anforderungen zuschneide. Also, je nachdem wie meine Anwendung das braucht. Zum Beispiel, wenn ich nach bestimmten Eigenschaften von Dokumenten suchen möchte, dann würde ich typischerweise in eine Dokumentendatenbank oder eine SQL-Datenbank nehmen. Dann kann es natürlich sein, ich habe Volltextsuchen. Dann würde ich sowas wie Elastic Search vorziehen. Es kann sein, ich habe Beziehungen, weil ich ein Social Network habe, dann nehme ich eine Graphendatenbank zum Beispiel. Oder ich möchte bestimmte Veränderungen über eine Zeit visualisieren, dann würde ich das typischerweise mit einer Time-Series-Datenbank machen. Oder auch so Geschichten wie Big Data Analytics, dass ich ein Hadoop dahinter habe oder sowas. Das können jetzt alles verschieden Write Models (Anm.: gemeint waren hier die Read Models) sein, wo meine Anwendung vielleicht eins davon braucht, um meine Queries einfach daraufhin optimieren zu können. Es kann aber auch sein, dass ich mehrere da davon brauche. Also, dass ich sowohl Big Data Analytics als auch eine Volltextsuche hab. Und durch die Trennung einfach, erreiche ich eine wesentlich bessere Skalierbarkeit. Also, ich kann einfach Write und Read Model auch unabhängig voneinander skalieren. Wenn ich jetzt zum Beispiel eine Anwendung habe, die sehr viel schreibt aber nur gelegentlich mal sucht, dann kann ich einfach sagen: Okay, meine Datenbank, die das Write unterstützt – also meine Cassandra zum Beispiel – da habe ich extrem viele Knoten, da habe ich ein potentes Cluster dahinter, und ich habe vielleicht nur zwei, drei Knoten die jetzt dann den Elastic Search Index bilden. Es hängt halt immer von den Anforderungen der Domäne ab. Aber ich kann es hier halt damit abbilden.

Lucas Dohmen: OK, und damit ist man dann sowohl bei der Abfrage flexibler als auch bei der Skalierung.

Niko Will: Genau.

Lucas Dohmen: Kannst du dann nochmal ganz kurz erläutern, wie du dann das Zusammenspiel zwischen dem Event Sourcing und CQRS in einer Anwendung – wie kann man sich das vorstellen? Kannst du da mal was beschreiben, wie ich mir das vorstellen kann?

Niko Will: Natürlich. Ich hab dann die sogenannten Commands, die werden zuerst gegen den aktuellen Zustand des Systems validiert. Also, ein Command kommt rein, ein Benutzer möchte irgendetwas tun, und dann kann ich zum Beispiel erst prüfen, hat der Benutzer überhaupt die Rechte, dieses oder jenes überhaupt zu tun. Oder kann eine Bestellung verschickt werden, weil alle Artikel schon da sind. Also solche Sachen. Wenn ein Command reinkommt, kann ich das validieren gegen den Zustand des Systems. Wenn diese Validierung erfolgreich ist, dann wird der Zustand geändert. Also ein Command hat die Intention, den Zustand zu ändern. Ein Command führt immer entweder als ganzes zum Erfolg, oder schlägt auch als ganzes fehl. Wenn jetzt so ein Command erfolgreich validiert wurde, dann habe ich eine Zustandsänderung. Wenn wir es mit Event Sourcing zusammen machen, dann führt diese Zustandsänderung eben zu einem neuen Event. Dieses Event wiederum wird an die sogenannten – also im CQRS nennt man das Event Handler – weitergeleitet. So einen Event Handler habe ich in der Regel je Read Model einen, der sich dann darum kümmert, dass dieses Event auch auf den Such-Index, also auf das Read Model angewendet wird. Der Benutzer zum Beispiel ändert seine Adresse, dieses Beispiel, wo wir vorher hatten, wird dann auch auf den Such-Index angewendet, dass wenn der Benutzer jetzt seine Daten sehen möchte, dass er da dann auch auf seiner aktuellen Adresse zum Beispiel suchen kann. Das nennt man auch das Materialized Views Pattern. Aber andere Event Handler können zum Beispiel auch abhängige Microservices sein. Die können dann so einen Event Handler implementieren, und können dann eben auf Zustandsänderungen von anderen Services reagieren.

Lucas Dohmen: Ok, ich verstehe. Wir haben jetzt sehr viel über die Theorie und auch über diese Beispiele gesprochen, ich denke mal wenn man solche Sachen in der Praxis umsetzen will, dann gibt es dann doch noch einige Dinge, die man vielleicht beachten müsste.

Niko Will: Ja genau, ein typisches Beispiel wäre dann auch so eine Konsequenz, wie Eventual Consistency. Die dann einfach ja, jetzt denke ich mal den Rahmen dieser Episode sprengen würde, aber das man sich auf jeden Fall dann auch noch genauer angucken sollte.

Lucas Dohmen: Ok. Dann würde ich doch vorschlagen, wir laden dich direkt nochmal zu einer weiteren Episode ein.

Niko Will: Sehr gerne.

Lucas Dohmen: Und dann erläutern wir, wie man diese Dinge in der Praxis einsetzen kann. Dann danke ich dir für dieses Gespräch, und für die Erläuterungen, und den Zuhörern bis zum nächsten Mal. Auf Wiedersehen!

Niko Will: Bis zum nächsten Mal. Dankeschön!