Podcast

Event Sourcing und CQRS – Teil 2

Implementierung und Architektur

In dieser Folge unterhalten sich Niko Will und Lucas Dohmen zum zweiten Mal über Event Sourcing und Command Query Responsibility Segregation, kurz CQRS. Nachdem wir in Teil 1 über die Vor- und Nachteile sowie einige technische Details gesprochen haben, geht es dieses Mal mehr um die Besonderheiten auf Ebene der Architektur. Wie fügen sich diese Patterns in eine verteilte Architektur ein? Auf was muss ich bei der Implementierung achten? Wie erreiche ich eventual consistency in Event-getriebenen Architekturen?
Listen to other episodes

Shownotes & Links

siehe auch die Shownotes zu Teil 1

Weitere Links zum Thema Schemamigration:

Interessante Artikelserie von Dennis Doomen (@ddoomen):

Ergänzung zu Frameworks aus dem 1. Teil:

Transkript

show / hide transcript

Niko Will: Hallo Lucas!

Lucas Dohmen: Wir hatten dich letztlich schon mal zu Gast. Da haben wir über Event Sourcing und CQRS gesprochen. Wir haben gedacht, wir laden dich nochmal ein, um noch ein paar Sachen, die offen geblieben sind, zu klären. Bevor wir anfangen, kannst du vielleicht noch mal eine kurze Zusammenfassung davon geben, was wir beim letzten Mal besprochen haben?

Niko Will: Ja klar, kann ich gerne machen. Ist ja auch schon wieder ein bisschen Zeit vergangen, seit wir die letzte Folge aufgenommen haben. Von dem her hat sich auch mein Mindset zwischenzeitlich geändert. Oder aktualisiert, würde ich sagen.

Ich würde es jetzt mal von der Architekturseite her sehen. Wir haben ja viel über die Patterns gesprochen, und was man mit den Patterns machen kann. Ich würde es gern dieses Mal von der anderen Seite kurz noch mal zusammenfassen. Die Probleme, die wir ja eigentlich haben oder adressieren wollen, dass wir verteilte Anwendungen haben – wir sprechen immer von Microservices, Self-contained Systems usw. – die machen ja meistens auch nichts alleine, die wollen ja schon interagieren mit anderen Services. Und dann kommt man an den Punkt, wo man sagt, “Ok, wenn sich in dem einen Service was ändert, dann muss ich das den anderen Services irgendwie mitteilen können”. Und dann ist eine Idee vom Domain-driven Design, das zum Beispiel über Domain Events zu machen. Also sobald sich der State in einem Service geändert hat, dass er diesen über Events, über einen Message-BUS, oder wie auch immer, anderen Services mitteilen kann. Damit die dann eben entsprechend darauf reagieren können.

Und da ist es einfach so, dass man sagt “Ok, wenn ich schon Domain Events veröffentliche, dann kann ich auch überlegen, wie persistiere ich denn intern meinen Status”. Und dann kommt man vielleicht auch auf den Punkt, dass man sagt, “Ich könnte ja auch diese Events, die ich publishe, veröffentliche, auch intern nehmen, um meinen State zu persistieren”. Indem ich einfach ein Event nach dem anderen, das ich publishe, auch schreibe, in einen Event Store. Und wenn ich jetzt meinen Zustand wiederherstellen möchte, dann lese ich einfach diese Events in Reihenfolge wieder ein und stelle meinen eigenen Zustand wieder her. Klar, da bist du dann natürlich an dem Punkt, wo man sagt, “Ich muss ja vielleicht dann viele Events lesen, und das kostet Zeit”. Das ist abhängig von der Anwendung. Manche Anwendungen werden tatsächlich nur ein, zwei Events haben, da ist das gar kein Drama, die noch mal einzulesen. Ansonsten kann man aber auch hergehen und mit Snapshots arbeiten. Ich nehme mir einmal alle fünf Minuten den aktuellen Zustand, und schreibe den noch mal in einen Snapshot kompakt weg. Dann muss ich nicht mehr so viele Events lesen, sondern nur die Events, die sich seit diesem letzten Speichern des Snapshots ergeben haben.

Ein Problem ist dann natürlich, wenn ich die Events so in diesem Format speichere, dass ich ein Problem habe, wenn ich Suchen machen möchte. Also ich möchte meinen Service auch mal abfragen und möchte da zum Beispiel typische Volltextsuche oder irgendwas machen. Und das ist mit Event Sourcing oder mit gespeicherten Events nicht mehr so einfach. Da gibt es dann aber wieder das, was wir letztes Mal auch schon angesprochen haben, das CQRS Pattern. Wo ich das Write Model und das Read Model trenne. Und eventuell auch mehrere Read Models haben kann, die einfach für Abfragen optimierte Datenbanken verwenden. Und ich damit dann diese Abfragen leichter ermöglichen kann. Und kann aber gleichzeitig, um diese Read Models abzudaten – die müssen ja dann auch auf die neusten Änderungen des States aktualisiert werden – wieder die Events nehmen, die ich eh veröffentliche. Darum sind das einfach zwei sehr schöne Patterns, die sehr gut zusammenarbeiten.

Lucas Dohmen: Wir hatten beim letzten Mal an der Stelle aufgehört, wo wir gesagt haben, “Wenn man das in der Praxis machen möchte, dann gibt es natürlich noch ein paar Dinge zu beachten”. Das was mir da als erstes einfallen würde, wäre die Frage: Brauche ich jetzt ein Framework? Oder kann ich das einfach so machen? Wenn ich ein Framework brauche, welches brauche ich dann? So in die Richtung.

Niko Will: Genau. Da bin ich genau an dem Punkt, wo ich sage: Ok, da habe ich mein Mindset ein bisschen aktualisiert. Also, ich komme einfach aus einer Domäne, wo es um sehr viele Events pro Sekunde geht beim Event Sourcing. Und drum bin ich es eher gewöhnt, dass ich da dann ein Framework nehme, wo der aktuelle Zustand auch im Speicher gehalten wird. Einfach weil ich, wenn ich tausend Events pro Sekunde habe, die ich im Event Store persistiere, und möchte dann den State wieder einspielen – tausend Events jedes Mal einzulesen, das ist einfach nicht machbar. Deswegen habe ich den State im Speicher, und da helfen mir Frameworks, wie zum Beispiel Akka. Lagom setzt auf Akka auf und macht vieles dann nochmal ein bisschen einfacher. Also, das sind jetzt typische Java/Scala Frameworks, aber das gibt es auch in anderen Sprachen. Ich glaube in .NET ist es Orleans oder so ähnlich. Weiter darüber hinaus kenne ich mich jetzt nicht aus. Aber da gibt es bestimmt in den meisten Sprachen dann auch irgendwie ähnliche Frameworks, die einem da helfen können.

Allerdings muss ich sagen, was ich in Gesprächen mitbekomme – nicht jede Anwendung hat diese tausenden von Events. Manche haben auch wirklich tatsächlich nur zwei, drei Events für ein bestimmtes Aggregate. Und da ist es dann auch gar kein Problem, wenn ich einfach jedes Mal, wenn ich diesen Zustand brauche, mir diese zwei, drei Events wieder einspiele, nacheinander anwende oder dann eben mit Snapshots arbeite. Da ist es jetzt wirklich nicht unbedingt notwendig, ein Framework zu verwenden, das kann ich in jeder Programmiersprache direkt machen. Da brauche ich auch nicht unbedingt eine spezielle Datenbank. Da kann man sich beliebig austoben, würde ich sagen.

Lucas Dohmen: Das andere Thema, über das wir kurz gesprochen haben, war Eventual Consistency. Das ist ja ein Phänomen, sage ich mal, allgemein gesprochen, was auftritt, wenn man verteilte Systeme hat. Vielleicht ist das nicht jedem Hörer ein Begriff. Kannst du das kurz erläutern, was du darunter verstehst?

Niko Will: Im Prinzip geht es ja darum, wir haben mehrere – wie angesprochen, verteiltes System – wir haben mehrere Microservices, zum Beispiel. Jetzt habe ich den Microservice, der sich zum Beispiel um die Kundendaten kümmert. Der Kunde hat seine Adresse geändert. Jetzt wird das in diesem einen Microservice verarbeitet, der für den Kunden zuständig ist. Der persistiert das auch, und der hat auch den aktuellen Zustand, und es ist alles gut. Jetzt habe ich aber vielleicht einen anderen Microservice, der die Kundendaten auch braucht. Wenn jetzt auf diesen Microservice zugegriffen wird, sehr zeitnah, nachdem sich in dem Kunden-Microservice der Kunde geändert hat, dann kann es einfach sein, dass dieser andere Microservice noch nicht die wirklich allerletzte, aktualisierte Version des Kunden hat. Und dann kann es natürlich dazu kommen, dass zum Beispiel der Kunde gerade frisch bestellt hat, und er lässt sich jetzt die letzten Bestellungen anzeigen, und da ist die allerletzte Bestellung noch nicht dabei. Solche Phänomene können dann auftreten. Und das ist aber wirklich dann ein Problem. Oder nicht unbedingt ein Problem, sondern das ist eine fachliche Anforderung. Also das muss die Fachlichkeit entscheiden, ob sie mit sowas umgehen kann, ob das schlimm ist oder nicht. Weil generell, wie du gesagt hast, in verteilten Systemen tritt sowas einfach sehr gerne auf.

Die wenigen Möglichkeiten, die man hat, das zu umgehen, wären verteilte Transaktionen. Und das in einem verteilten System ist einfach sehr langsam und skaliert sehr schlecht. Drum ist Eventual Consistency eigentlich kein Schimpfwort. Das ist eigentlich eher eine Garantie, die einem das Leben leichter machen kann, weil man weiß – irgendwann in der Zukunft wird ein Zustand eintreten, wo wieder alle Systeme untereinander konsistent sind. Es kann eben, wenn man diese Regeln einhält, nicht passieren, dass die Services völlig inkonsistent auseinanderlaufen. Und das ist eigentlich eine sehr schöne Garantie, finde ich.

Lucas Dohmen: Aber das bedeutet ja auch, wenn ich diese Garantie erreichen möchte – das geht ja nicht von selbst. Es ist ja nicht so, dass das quasi der Standardzustand ist. Was muss ich denn tun, um so einen Zustand zu erreichen? Wenn das für meine Fachlichkeit in Ordnung ist, Eventual Consistency zu haben und nicht volle Consistency? Was muss ich tun, damit ich das erreiche, wenn ich CQRS und Event Sourcing mache?

Niko Will: Auf der einen Seite ist es bei CQRS und Event Sourcing eigentlich schön, dass es einem ein bisschen dabei hilft. Weil es natürlich so ein opinionated way auch ist, wie ich sowas modellieren kann. Aber klar, ich muss natürlich einige Dinge da beachten. Punkt 1 ist: Ich habe meinen Microservice, da persistiere ich das Event in den Event Store. Und dann muss ich das Event publishen, damit die anderen Services das lesen können. Und sich selber auch aktualisieren können. Jetzt kann es natürlich zu vielen Problemen kommen. Also einerseits, ich habe das Event jetzt persistiert, in meinem Event Store, und möchte das nun publishen. Jetzt kann es sein – Anwendungen sterben – in dem Moment, wo ich es eigentlich publishen möchte, stürzt meine Anwendung ab. Aus welchen Gründen auch immer. Klar, ich habe wahrscheinlich irgendwelche Systeme außenrum, die das monitoren und dann auch sicherstellen, dass die Instanz wieder hochgefahren wird. Aber jetzt muss ich an der Stelle ja auch wissen, dass das Event zwar persistiert, aber noch nicht gepublished wurde. Also muss ich das jetzt nochmal nachholen. Und das ist schon etwas, was beliebig komplex werden kann. Je nach Anforderung, auch.

Ich kann hier natürlich, in sehr einfachen Szenarien, wenn ich wenige Events habe, auch einfach hergehen und sagen, “Ok, ich mache das in einer Transaktion”. Typisch Spring zum Beispiel, ich mache ein @Transactional dran und persistiere das einerseits in die Datenbank, und im nächsten Schritt publishe ich das zum Beispiel auf einen Message Broker wie Kafka. Und wenn ich von Kafka das acknowledge bekommen habe, dann kann ich die Transaktion verlassen, und damit wird es auch definitiv in die Datenbank geschrieben. Wenn da jetzt irgendwas zwischendrin schiefgehen würde, dass das Event nicht gepublished wurde, weil vielleicht zum Beispiel auch der Message Broker nicht da ist, dann würde die Transaktion abgebrochen und das Event würde auch gar nicht persistiert werden. Und gleichzeitig würde ich damit auch den ursprünglichen Request – also zum Beispiel der Benutzer der seine Adresse ändern wollte – mit einem Fehler terminieren. Dann müsste er das halt nochmal machen, zum Beispiel. Das wäre jetzt eine recht einfache Möglichkeit, die in vielen Fällen bestimmt schon ausreichen kann.

Es gibt andere Möglichkeiten, ich kann natürlich sagen, “Ja also, ich verzichte auf irgendwelche Schutzmechanismen”. Ich möchte wirklich die Events einfach nur rausblasen. Dann kann es natürlich passieren, es gehen Events verloren. Und damit müssen natürlich andere Systeme, die auf diese Events angewiesen sind, umgehen können. Die Events, das haben wir ja letztes Mal erwähnt, haben eine sequence number. Die sind durchnummeriert. Jetzt kann ja einfach ein anderer Service sagen, “Wenn jetzt zwischendrin mal ein Event verloren geht, dann merke ich das”. Weil die sequence number – da kam die 11 und dann plötzlich kommt die 13. Das heißt, Event 12 ist irgendwo verloren gegangen. Das heißt, dann kann das andere System einfach sagen, “Ok, ich triggere den Herr-der-Daten-Microservice, gib mir doch mal den aktuellen Zustand”. Und dann kann ich alle Events, die vor diesem aktuellen Zustand liegen, wegschmeißen, weil ich habe den aktuellen Zustand ja dann live von dem System geholt.

Lucas Dohmen: Das ist ja ähnlich zu dem Snapshot, im Prinzip, ne?

Niko Will: Geht in die Richtung, genau. Das ist, wie wenn ich einen Snapshot von dem Herr-der-Daten-Microservice anfordern würde. Das kann ich quasi so machen, habe dann aber eventuell ein Problem, wo ich einfach an der Stelle nochmal drauf hinweisen möchte. Wenn jetzt das letzte Event verloren gegangen ist. Also, Microservice schickt Event 13 und 14 noch raus. Die abhängigen Microservices empfangen aber nur noch Event 13. Dann stellen sie es ja nicht fest, dass quasi das 14. Event fehlt. Das heißt, bei dieser Synchronisierung muss man auch beachten, dass man die zyklisch wiederholen sollte, um nochmal zu gucken, “Bin ich wirklich auf dem letzten Stand”. Man kann die Technik dann noch ein bisschen ausgefeilter machen, indem man sagt, “Ok, ich hole mir nicht das komplette Objekt/Aggregate/Zustand, ich hole mir vielleicht nur die letzte sequence number”. Und wenn ich sehe, die passt nicht zu der, die ich habe, dann synchronisiere ich. Das kann zum Beispiel eine Möglichkeit sein, so kann ich den etwas komplexeren Varianten aus dem Weg gehen.

Die da wären, dass ich wirklich sicher stellen muss, dass meine Events persistiert und dann gepublished werden. Das kann ich zum Beispiel auch erreichen, indem ich sage, “Ich publishe gar nichts selber”. Ich schreibe immer nur in den Event Store rein. Und es gibt einen anderen Service, oder Prozess, der an dem Event Store dranhängt und lauscht. Dieses Lauschen kann sein, dass es regelmäßig pollt – auch eine Art der Synchronisierung. Oder manche Datenbanken bieten Konnektoren, wo ich sagen kann, “Ich habe hier einen Query, das setze ich ab, und ich bekomme quasi einen Stream von neuen Daten”. Ich hänge mich ans Audit Log dran oder wie auch immer das je nach Datenbank implementiert wird. Aber das wäre auch eine Möglichkeit, dass ich mich da direkt dranhänge. Und die Events auslese, die neu reingekommen sind, und die dann publishe. Und wenn ich dann zum Beispiel einen Message Broker nehme, der mir einfach bestimmte Garantien gibt, wie zum Beispiel Kafka, wo die Sachen dann selber wieder persistiert werden. Dann ist eigentlich die Aufgabe dieses zweiten Prozesses, nur dafür zu sorgen, alle neuen Events zu lesen, in Kafka zu veröffentlichen, und dann aber sich selber auch nochmal persistent zu merken, “Dieses Event habe ich schon verarbeitet”. Damit habe ich erreicht, dass jedes Event, das persistiert wurde in die Datenbank, auch irgendwann in dem Message Broker landet. Und von dort aus dann von den anderen Microservices abgeholt werden kann.

Was da zu beachten ist, ich weiß gar nicht ob ich das letztes Mal angesprochen habe, hier kann es jetzt natürlich passieren, dass ich ein Event mehrfach verschicke. Weil es kann ja auch passieren – dieser Prozess, der dieses Event aus der Datenbank rausliest, published zu Kafka, und schafft es jetzt aber vielleicht nicht mehr, sich selber zu persistieren, “Hey dieses Event habe ich schon verarbeitet”. Das heißt, der würde neu starten, würde dann dieses Event wieder lesen, würde sagen “Ah ok, habe ich noch nicht verarbeitet, publishe ich mal auf Kafka”. Damit ist dieses Event jetzt potentiell zwei Mal gepublished oder vielleicht noch öfter. Das heißt, da ist es jetzt wichtig, dass die Event Handler – so nennt man sie ja im CQRS – die die anderen Events verarbeiten, also die anderen Microservices, damit umgehen können. Einfach, dass sie – man nennt es die Duplication – feststellen, “Ok, dieses Event Nummer 13 habe ich jetzt zweimal bekommen, wende ich nur einmal an”, zum Beispiel.

Gleichzeitig müssen aber auch die Microservices, die diese Events empfangen, sicherstellen, dass sie diese schon verarbeitet haben. Und da ist es am einfachsten, wenn man sagt, “Ich wende jetzt dieses Event auf meinen Datenbestand an, und während ich das in meiner Datenbank persistiere, schreibe ich auch die sequence number mit rein”. Damit habe ich mir automatisch gemerkt, welche Nachrichten ich schon verarbeitet habe. Da kann ich dann auch so Geschichten machen wie, “Update den State nur, wenn sequence number kleiner [größer? 17:30] als _”, je nachdem was die Datenbank da bietet. Genau. Das sind so Möglichkeiten, wie ich quasi diese Eventual Consistency Garantie herstellen kann. Das ist nicht einfach, das kann relativ komplex sein, aber es kann sich auch auszahlen. Und da muss man einfach entscheiden, ist es für mich wichtig, dass ich Transaktionen komplett vermeide, weil ich das Ganze sehr schnell brauche – dann muss ich halt eben diese komplexeren Sachen anwenden. Oder ich nehme einfach Transaktionen bei mir lokal, es ist zum Glück ja dann immer noch keine verteilte Transaktion. Damit in den meisten Fällen ok. Und habe damit dann auch Eventual Consistency sichergestellt.

Lucas Dohmen: Und wenn das jetzt aus Sicht meiner Fachlichkeit so ist, dass für den Großteil des Systems Eventual Consistency vollständig in Ordnung ist, aber es gibt so eine Stelle, an der brauche ich jetzt unbedingt Konsistenz. Würde das auch funktionieren? Dass ich dann sage, an der Stelle mache ich 'ne Transaktion? Oder widerspricht sich das?

Niko Will: Also prinzipiell widerspricht sich das nicht unbedingt. Unser Kollege Michael Plöd, zum Beispiel, hat mal auf einem Meetup einen sehr interessanten Vortrag über Event Sourcing und CQRS gehalten. Er kommt aus bisschen einer anderen Domäne oder hat andere Anforderungen gesehen als ich und für ihn war das jetzt zum Beispiel gar nicht wichtig, dass Events schnell geschrieben werden. Und er hat auch bei sich eine Möglichkeit genannt, wirklich eine Transaktion zu machen, sowohl über das Write Model, als auch über das Updaten der Read Models. Sprich: ich schreibe mir dieses Event in mein Write Model und innerhalb dieser Transaktion tue ich aber alle Read Models, die ich habe, aktualisieren. Somit kann ich natürlich eventuell Strong-Consistency-Garantien lokal für ein System garantieren.

Wenn es distributed wird, dann wird es meiner Meinung nach sehr schwierig. Ich kann natürlich versuchen, so Sachen zu machen wie verteilte Transaktionen. Also, ich wende das bei mir an, und dann publishe ich das, und erst wenn ich die Antwort bekommen habe, dass der andere das auch verarbeitet hat – aber da muss man dann wieder mit so Sachen umgehen können wie, was ist, wenn die Antwort unterwegs verloren geht, dann hat der eine das persistiert, der andere nicht. Und dann muss ich vielleicht wieder einen distributed rollback machen. Two-phase commits ist so ein Stichwort, wo man da gerne nach suchen kann, wenn man sich das antun möchte. Eventuell kann man auch mit CRDTs arbeiten, ist vielleicht eine andere Möglichkeit, habe ich selber jetzt noch nicht gemacht, könnte funktionieren. Ja. Schwierig aus meiner Sicht, lokal völlig in Ordnung, kann man machen, aber verteilt würde ich das nicht machen. Wenn ich ein verteiltes System habe, und habe aber so Anforderungen, dass es strong consistent sein muss – ja das könnte einen vor Herausforderungen stellen.

Lucas Dohmen: Aber das wäre dann vielleicht auch etwas, was man beim Schnitt der Systeme dann beachten muss.

Niko Will: Ja, richtig.

Lucas Dohmen: Ok. Das andere Thema, über das wir noch sprechen wollten, waren Schema-Migrationen. Wenn sich die Schemen – oder Schemata lacht der Events verändern, wie würdest du da vorgehen? Was hast du da für Techniken?

Niko Will: Prinzipiell habe ich da kein Patentrezept. Ich finde nur, es ist auch ein wichtiges Thema, das man auf jeden Fall ansprechen muss. Weil man diese Probleme irgendwann haben wird. Also, um was geht es da konkret? Ich speichere diese Events ja bei mir in der Datenbank. Ich persistiere die. Das heißt, ich persistiere die in einem bestimmten Format. Jetzt muss ich ja eigentlich, damit ich meinen State wiederherstellen kann, dieses Format immer lesen können. Das heißt, wenn ich das Format irgendwann mal ändere, dann muss ich das alte Format trotzdem noch unterstützen. Und das ist auch was, was man oft hört in dem Kontext, “Wie mache ich denn das, was gibt es da für Ideen”. Also, ich habe da jetzt auch noch gar nicht so arg viel gesehen, muss ich sagen. Die typischen Ideen, die man immer liest, sind, dass ich quasi sage, “Ok, von meinem Aggregate schreibe ich mir einen Snapshot und ändere dann das Format und unterstütze einfach über eine gewisse Zeit beide Formate”. Schreibe inzwischen mehrere Snapshots und irgendwann kann ich dann das alte Format in meiner Produktivanwendung langsam auslaufen lassen, das zu unterstützen. Weil ich die in meiner Produktivanwendung faktisch nicht mehr lesen muss. Ich lese dann immer von den Snapshots und ab da nur noch Events in neueren Formaten.

Prinzipiell ist es aber trotzdem gut, immer diese Formate noch weiterhin irgendwo dokumentiert zu haben, weil, ich werde die weiterhin lesen müssen. Also zum Beispiel, wenn ich, was wir letztes Mal angesprochen haben, Bugs entdeckt habe und muss dann eventuell eine ganze Reihe von Events wieder einspielen, um meinen Datenbestand zu korrigieren. Dann muss ich vielleicht wirklich alte Events wieder lesen können. Und da ist es dann wichtig, dass ich dieses Format dann noch beherrsche. In welcher Form auch immer.

Wovon ich absolut abraten würde, sind so Ideen wie, “Ach ja, wenn ich jetzt zum Beispiel den einen Feldnamen von meinem Event ändere, dann mache ich auf meiner Tabelle ein update auf alle vorhandenen und benenne jetzt dieses Feld um”. Da würde ich absolut davon abraten. Events sollte man von vornherein so behandeln wie Fakten, die sind in der Vergangenheit aufgetreten, die wurden persistiert und fertig. Die fasse ich nicht mehr an. Drum auch Schema-Migrationen – in dem Sinne ist das keine Schema-Migration. Das Schema ändert sich, aber ich migriere die alten Daten nicht. Ich muss damit umgehen können, dass es sich ändert das Format, da ist eben eine Möglichkeit die, die ich gerade genannt habe. Aber ich migriere es nicht wirklich. Und ich denke, wenn man das beachtet, dann hat man da jetzt auch nicht so die Schmerzen damit. Aber es ist gut, wenn man das zumindest im Hinterkopf hat, dass das irgendwann kommen wird.

Lucas Dohmen: Genau, dass man das einplant. Dass man zum Beispiel eine Versionsnummer von diesen Schema vielleicht –

Niko Will: Ja, genau, guter Hinweis. Auf jeden Fall in den Events selber – bestimmte Sachen sollten sich nicht ändern. Also, ein Event hat immer eine Aggregate-Id, also zum Beispiel die Customer-Id sollte im Event immer mit drin stehen. Es hat eine sequence number, die muss auch immer mit drinstehen, und wenn ich die einmal sequence number genannt habe, dann sollte ich die nicht in revision oder sowas umbenennen. Die sollte dann so bleiben. Was ich auch immer habe, ist der Event-Typ. Weil ein Event hat dann immer noch eine Payload. Und der Event-Typ gibt mir einfach an, wie ich die Payload zu interpretieren habe. Und dann darüber hinaus macht es natürlich Sinn, eine Art Version zu haben. Die kann ich dann einfach hochzählen, und dann weiß ich auch, “Ok, dieser Event-Typ, in dieser Version, dann sieht die Payload so und so aus”. Genau. Das ist ein guter Hinweis. Version lohnt sich auf jeden Fall von vornherein mit einzuplanen.

Lucas Dohmen: Ja, ok. Gibt es sonst noch was, was du gerne noch auf den Weg geben würdest, für jemanden, der dieses Themengebiet sich anschauen möchte oder das implementieren möchte?

Niko Will: Ich denke, wir werden einige Sachen in die Shownotes packen. Man findet sehr viel über die ganzen Themen. Microsoft hat in der MSDN eine sehr gute Beschreibung, wie ich finde, allerdings auf Englisch. Das werden wir auf jeden Fall in die Shownotes packen, das ist wirklich sehr ausführlich, die machen das anhand von so einem – wie nennen sie das – wie so ein Fallbeispiel. Anhand dessen erarbeiten sie so eine Anwendung, da finde ich ist es wirklich sehr schön erklärt. Aber ansonsten, bei allen Größen, Martin Fowler und wie sie alle heißen, schreiben darüber und haben da Einträge. Im Internet findet man da sehr viel dazu. Ich denke, das meiste haben wir jetzt genannt, und ansonsten können sich die Leser ja auch gerne an uns wenden. Und wenn wir merken, da sind noch viele Schmerzen da, dann spricht auch nichts dagegen, dass man noch eine dritte Episode macht lacht.

Lucas Dohmen: lacht Alles klar! Ja, dann danke ich dir.

Niko Will: Sehr gerne.

Lucas Dohmen: Und den Zuhörern bis zur nächsten Folge vom innoQ Podcast.

Niko Will: Viel Spaß!

Alumnus

Lucas was a senior consultant at INNOQ until August 2023. He works on the architecture, conception, and implementation of web applications on the front and back end. He programs in Ruby and JavaScript and helps with technology decisions & the adoption of different NoSQL solutions. Lucas is one of the authors of the book “The Rails 7 Way”. You can hear his voice on the INNOQ podcast quite regularly. He does open source and community work (like organizing and teaching at the local CoderDojo).

Alumnus

Niko Will worked as Senior Consultant for software architecture and engineering at INNOQ Germany until January 2022. He focuses on designing and implementing highly scalable, distributed software systems. Recently he is dedicated to domain-driven design, event driven architectures and reactive technologies.