DDD, Saga & Event-Sourcing: Kann eine Ausgleichsaktion einfach ein Löschen im Event-Store sein?


15

Mir ist klar, dass die obige Frage wahrscheinlich ein paar Fragen aufwirft, aber lassen Sie mich versuchen zu erklären:

Ich versuche, mich auf ein paar verwandte Konzepte einzulassen, im Grunde das Saga-Muster ( http://www.rgoarchitects.com/Files/SOAPatterns/Saga.pdf ) in Kombination mit Event-Sourcing (Ein DDD-Konzept) : http://en.wikipedia.org/wiki/Domain-driven_design )

Ein guter Beitrag, der alles zusammenfasst: https://blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/

Ich komme gleich zu der Frage, aber ich denke, ich muss versuchen, zunächst zusammenzufassen, was ich davon verstehe (was möglicherweise falsch ist, korrigieren Sie dies bitte, wenn dies der Fall ist), da dies möglicherweise einen Einfluss darauf hat, warum ich es bin die Frage stellen, um mit zu beginnen:

  1. Das Saga-Muster ist eine Art Broker, der bei einer bestimmten Aktion (Endbenutzer, Automatisierter usw., im Wesentlichen alles, was Daten ändert) diese Aktion in Geschäftsaktivitäten aufteilt und jede dieser Aktivitäten als Nachrichten an einen Nachrichtenbus sendet, der Sendet es wiederum an die jeweiligen Aggregatwurzeln, um es zu pflegen.
  2. Diese aggregierten Wurzeln können völlig autonom arbeiten (nette Trennung von Bedenken, große Skalierbarkeit usw.)
  3. Eine Saga-Instanz selbst enthält keine Geschäftslogik, die in den aggregierten Wurzeln enthalten ist, an die sie Aktivitäten sendet. Die einzige 'Logik', die in der Saga enthalten ist, ist die 'Prozess'-Logik (oft als Statemachine implementiert), die basierend auf empfangenen Aktionen (sowie Folgeereignissen) festlegt, was zu tun ist (dh welche Aktivitäten gesendet werden sollen).
  4. Das Saga-Muster implementiert eine Art verteiltes Transaktionsmuster. Dh, wenn eine der aggregierten Wurzeln (die wiederum autonom arbeiten, ohne die Existenz der anderen zu kennen) ausfällt, muss möglicherweise die gesamte Aktion zurückgesetzt werden.
  5. Dies wird umgesetzt, indem alle aggregierten Wurzeln nach Abschluss ihres Aktivitätsberichts an die Saga zurückgeschickt werden. (Im Erfolgsfall ebenso wie im Fehlerfall)
  6. Für den Fall, dass alle aggregierten Wurzeln erfolgreich sind, bestimmt die interne Statemachine, ob die Saga als Nächstes etwas unternimmt (oder entscheidet, ob dies getan wird).
  7. Im Fehlerfall gibt die Saga an alle Aggregatwurzeln, die an der letzten Aktion teilgenommen haben, eine sogenannte Ausgleichsaktion aus, dh: eine Aktion, um die letzte Aktion rückgängig zu machen, die jede der Aggregatwurzeln ausgeführt hat.
  8. Wenn die Aktion "plus 1 Stimme" war, könnte dies einfach eine "Minus 1 Stimme" sein, aber es könnte komplizierter sein, als wenn ein Blogpost auf seine vorherige Version zurückgesetzt würde.
  9. Event-Sourcing (siehe den Blogpost, in dem beide kombiniert werden) zielt darauf ab, die Ergebnisse der einzelnen Aktivitäten, die jeder der zusammengefassten Roots durchführt, in einem zentralen Event-Speicher zu speichern (die Änderungen werden in diesem Kontext als "Ereignisse" bezeichnet).
  10. Dieser Ereignisspeicher ist die "einzelne Version der Wahrheit" und kann verwendet werden, um den Status aller Entitäten abzuspielen, indem einfach die gespeicherten Ereignisse wiederholt werden (im Wesentlichen wie ein Ereignisprotokoll).
  11. Die Kombination der beiden Möglichkeiten (dh das Auslagern der Änderungen mithilfe von Event-Sourcing durch aggregierte Roots, bevor sie an die Saga zurückgemeldet werden) bietet viele nette Möglichkeiten, von denen eine meine Frage betrifft ...

Ich hatte das Gefühl, ich musste das von meiner Schulter nehmen, da es eine Menge ist, die ich auf einmal begreifen kann. In Anbetracht dieses Kontexts / dieser Einstellung (bitte korrigieren, falls falsch)

Die Frage: Wenn ein aggregierter Stamm eine Kompensationsaktion erhält und dieser aggregierte Stamm seine Zustandsänderungen mithilfe von Ereignisquellen ausgelagert hat, wäre die Kompensationsaktion nicht in allen Situationen einfach eine Löschung des letzten Ereignisses im Ereignisspeicher Aggregate Root gegeben? (Vorausgesetzt, die Persist-Implementierung erlaubt das Löschen)

Das würde für mich viel Sinn machen (und wäre ein weiterer großer Vorteil dieser Kombination), aber wie gesagt, ich könnte diese Annahmen auf der Grundlage eines falschen / unvollständigen Verständnisses dieser Konzepte treffen.

Ich hoffe, das wurde nicht zu langatmig.

Vielen Dank.

Antworten:


9

Sie sollten die Ereignisse nicht aus Ihrem Ereignisspeicher löschen. Diese repräsentieren etwas, was passiert ist. Wenn jetzt etwas passiert, sollten Sie es auch wissen. Fügen Sie daher ein Ereignis hinzu, das Ihr Aggregat zwingt, zu einem früheren Zustand zurückzukehren. Es ist schade, Informationen aus Ihrer Datenbank zu löschen.

Denken Sie an das Beispiel des Wagens von Greg Young und die entfernten Gegenstände. Möglicherweise ist die Information der Ausgleichsmaßnahme morgen von beliebigem Wert.

Was die Saga angeht, so ist, soweit ich mich kenne, alles in Ordnung. Sie sollten sich die Saga als eine Zustandsmaschine vorstellen. Es macht nur das.

Es ist ein Befehl, den die Saga ausgibt, um das Aggregat anzuweisen, etwas zu tun. Von dieser Aktion wird es eine Veranstaltung geben. Dieses Ereignis ist möglicherweise die erforderliche Korrekturmaßnahme oder muss in einer Ansicht denormalisiert werden, damit einige Benutzer es sehen und entscheiden können, was zu tun ist.

Es hängt von Ihren Geschäftsregeln ab. Entweder gibt es eine einfache Lösung: Der Gesamtstamm weiß, dass Sie dies in einem solchen Fall tun, oder die Auswahl ist zu komplex, um programmgesteuert durchgeführt zu werden (die Entwicklungskosten sind zu hoch), und daher können Sie dies einem Benutzer vorschlagen, der Möglicherweise können Sie zwischen verschiedenen Aktionen wählen. Alles hängt vom Kontext der Ausgleichsmaßnahme ab. Dies sind reine Geschäftsregeln. Was sollen wir in diesem oder jenem Fall tun? Fragen Sie Ihren Domain-Experten, und entscheiden Sie dann mit ihm, ob es besser ist, eine automatische Lösung zu entwickeln, oder ob der Benutzer Ronald R. gefragt werden soll, da er diese Frage in der Regel beantwortet.


Wie würde das Aggregat wissen, in welchen Zustand es zurückkehren soll? Würde es erlaubt sein, Fragen an den Event-Store zu richten oder so?
Geert-Jan

Es sollte nicht rückgängig gemacht werden, sondern ein weiteres Ereignis hinzugefügt werden. Es ist ein Befehl, den die Saga ausgibt, um das Aggregat anzuweisen, etwas zu tun. Von dieser Aktion wird es eine Veranstaltung geben.
Arthis

Ja ich verstehe. "Revert" wurde wahrscheinlich falsch gewählt. Beachten Sie Folgendes: Möglicherweise möchten Sie Event Sourcing mit Sagas für die Modellierung von Dokumentenveröffentlichungs- / Genehmigungsabläufen verwenden. Was ist, wenn ein Editor eine ChangeBlogBody-Aktion an den Event Store gesendet hat, die letztendlich als BlogBodyChanged-Ereignis beibehalten wird. Aus irgendeinem Grund wird anschließend eine Ausgleichsaktion ausgegeben. Wie würde das Aggregat wissen, welches Ausgleichsereignis ausgelöst wird, das zum Inhalt des Blog-Texts führt, wie es unmittelbar vor der Ausgabe der ChangeBlogBody-Aktion war?
Geert-Jan

Es hängt von Ihren Geschäftsregeln ab. Entweder gibt es eine einfache Lösung: Der Gesamtstamm weiß, dass Sie dies in einem solchen Fall tun, oder die Auswahl ist zu komplex, um programmgesteuert durchgeführt zu werden (die Entwicklungskosten sind zu hoch), und daher können Sie dies einem Benutzer vorschlagen, der könnte zwischen verschiedenen Aktionen wählen.
Arthis

Alles hängt vom Kontext der Ausgleichsmaßnahme ab. Dies sind reine Geschäftsregeln. Was sollen wir in diesem oder jenem Fall tun? Fragen Sie Ihren Domain-Experten, und entscheiden Sie dann mit ihm, ob es besser ist, eine automatische Lösung zu entwickeln, oder ob der Benutzer Ronald R. gefragt werden soll, da er diese Frage in der Regel beantwortet.
Arthis

6

Der Vollständigkeit halber habe ich mir überlegt, ein relevantes Snippet von Martin Fowler über Möglichkeiten zur Wiederherstellung des Zustands beizufügen:

Neben den Ereignissen, die sich vorwärts abspielen, ist es oft nützlich, dass sie sich selbst umkehren können.

Die Umkehrung ist am einfachsten, wenn das Ereignis in Form einer Differenz gewirkt wird. Ein Beispiel hierfür wäre "10 USD zu Martins Konto hinzufügen" im Gegensatz zu "Martins Konto auf 110 USD festlegen". Im ersteren Fall kann ich durch Abzug von 10 US-Dollar den Vorgang rückgängig machen, im letzteren Fall habe ich jedoch nicht genügend Informationen, um den früheren Wert des Kontos wiederherzustellen.

Wenn die Eingabeereignisse nicht dem Differenzansatz folgen, sollte das Ereignis sicherstellen, dass alle für die Umkehrung während der Verarbeitung erforderlichen Daten gespeichert werden. Sie können dies tun, indem Sie die vorherigen Werte für jeden Wert speichern, der geändert wird, oder indem Sie Differenzen für das Ereignis berechnen und speichern.

Von: http://martinfowler.com/eaaDev/EventSourcing.html


1

Konzeptionell:

  • Ereignis ist etwas, was passiert ist. Die Vergangenheit kann nicht geändert werden.
  • Befehl ist etwas zu tun. Befehl kann nicht passieren (kann abgelehnt werden).

Wir können unseren zukünftigen Status nur ändern, indem wir einen anderen Befehl ausführen (Aktion kompensieren), der dazu führen sollte, dass Ereignisse den Status der Anwendung ändern.

Um die Frage zu "verwirren", denken Sie an diesen Ausdruck "Löschen des letzten Ereignisses":

  • Was ist, wenn das letzte Ereignis nicht gelöscht werden muss? Was passiert, wenn andere Ereignisse nach dem zu löschenden eintreten? Diese Ereignisse müssen ebenfalls geändert werden (da sich ihr Basiszustand geändert hätte, indem ein vorheriges Ereignis gelöscht worden wäre).
  • Was ist, wenn das Ereignis an ein externes System gesendet wurde? Möglicherweise haben Sie keinen anderen Zugriff, um den Status zu korrigieren, als ein anderes Ereignis zu senden.

Kurz gesagt, Sie haben keine Möglichkeit, Ereignisse innerhalb des CQRS-Musters zu löschen.

Sie können den Ereignisspeicher verkleinern, indem Sie einen Snapshot-Status erstellen (der auf allen Ereignissen basiert, die zu diesem Status führen). Dieser physikalische Aspekt hat jedoch nichts mit dem Konzept des Ereignisses zu tun.


0

Geert-Jan, ich denke auch, dass die Ausgleichsaktion einfach die entsprechenden Ereignisse löschen kann. Es ist sinnvoll und zeigt einen weiteren Vorteil des Event-Sourcing-Entwurfsmusters: die einfachere Implementierung des Entwurfsmusters "Compensating Transaction".

Einige sagen, das Löschen des Ereignisses verstoße gegen die "Prinzipien" des Event-Sourcing oder des CQRS. Ich denke, das ist eine begrenzende Verallgemeinerung. Ich denke, es ist in Ordnung, ein Ereignis zu löschen, wenn es im Rahmen einer globalen Transaktion erstellt wurde, die storniert wird. Betrachten Sie den Pseudocode:

try {
    newEvents = processMycommand(myCommand)
    for (Event newEvent : newEvents)
        EventStore.insertEvent(newEvent);
} catch (Exception e) {
    EventStore.rollback();
}

Angenommen, Ihr Ereignisspeicher ist eine Transaktionsdatenbank. Im Pseudocode können Sie sich die Situation vorstellen, in der Sie das erste Ereignis eingefügt haben, aber beim Versuch, das zweite Ereignis einzufügen, wurde eine Ausnahme ausgelöst. Der Rollback-Befehl würde natürlich das Einfügen des ersten Ereignisses rückgängig machen. Ist das vernünftig?

Wenn es für eine ACID-Datenbanktransaktion (Transaktion mit Festschreiben / Zurücksetzen) angemessen ist, warum wäre es für eine Ausgleichstransaktion nicht angemessen?

Bei der Ausführung der globalen Saga-Transaktion können Datenänderungen rückgängig gemacht werden (durch Kompensation). Das Ereignis, das während der Transaktion erstellt wurde, muss nicht beibehalten werden, da die Transaktion nicht abgeschlossen wurde.

Wenn die Kompensation nun versucht, ein Ereignis zu löschen, und dieses Ereignis nicht das neueste Ereignis auf einem Objekt ist, sollte das Löschen nicht erfolgen. Im Allgemeinen ist dies jedoch unwahrscheinlich, insbesondere bei leseintensiven Lösungen.


0

Lassen Sie uns mit dem Gedanken spielen, dass der Ereignisspeicher nur Daten sind, die Sie speichern möchten. Zum Beispiel können wir eine Festplatte haben, am Anfang beginnen und Daten darauf schreiben. Jedes Mal, wenn wir ein Ereignis erhalten, hängen wir unsere vorherigen Daten an, sodass wir einfach weiter auf die Festplatte schreiben, auf der wir das letzte Mal angehalten haben. Wenn wir ein Ereignis entfernen möchten, gehen wir zurück, entfernen Sie diesen Teil von der Festplatte und lassen Sie eine Lücke. Das Zurückbewegen von alleine verlangsamt unseren Veranstaltungsspeicher, aber damit können wir leben. Wenn wir ein Dateisystem verwenden, wird versucht, die Lücke mit letzteren Ereignissen zu füllen, sodass wir am Ende einen langsamen fragmentierten Speicher haben oder eine Defragmentierung durchführen können. Keiner von ihnen ist etwas, was wir mögen. Wenn es sich um Datenbanken handelt, erhalten Sie dies, wenn Sie eine relationale Datenbank anstelle einer Datenbank zum Anhängen verwenden, in der Ihre Ereignisse gespeichert werden:

Bildbeschreibung hier eingeben ref: https://cambridge-intelligence.com/bringing-time-series-data-to-life-with-keylines/

Ofc. Für eine normale Site ist das egal, aber diese Lösungen sind für riesige Websites wie Facebook, Google usw. konzipiert. Die Frage ist also nicht wirklich sinnvoll, denn wie würden Sie ein Ereignis in einer Append-Only-Datenbank löschen und wie würden Sie das tun Sie nennen Kompensation so etwas wie den Bau einer Zeitmaschine und das Zurückversetzen in die Vergangenheit, um ein Ereignis zu ändern oder zu verhindern?

So viel ich weiss. Die einzige Möglichkeit, dies zu beheben, besteht darin, einen neuen Ereignisspeicher zu erstellen, in dem Sie die nicht gewünschten Ereignisse ausschließen und anschließend den alten Ereignisspeicher entfernen. Dies ist jedoch eine extreme Lösung, um ein einzelnes Ereignis zu entfernen. Wenn es sich um GDPR handelt, ist die einzige mir bekannte, relativ gute Lösung, persönliche Daten verschlüsselt im Ereignisspeicher zu speichern und den Verschlüsselungsschlüssel aus einer anderen Datenbank zu entfernen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.