CQRS + Event Sourcing: (ist es richtig) Befehle werden im Allgemeinen Punkt-zu-Punkt übertragen, während Domänenereignisse über Pub / Sub übertragen werden?

11

Ich versuche im Grunde, mich mit dem Konzept von CQRS und verwandten Konzepten zu beschäftigen.

Obwohl CQRS Messaging und Event Sourcing nicht unbedingt beinhaltet, scheint es eine gute Kombination zu sein (wie aus vielen Beispielen / Blogposts hervorgeht, die diese Konzepte kombinieren).

Würden Sie angesichts eines Anwendungsfalls für eine Statusänderung für etwas (z. B. um eine Frage zu SO zu aktualisieren) den folgenden Ablauf als korrekt betrachten (wie in der Best Practice)?

Das System gibt einen aggregierten UpdateQuestionCommand aus, der möglicherweise in einige kleinere Befehle unterteilt ist: UpdateQuestion, das auf den Fragenaggregatstamm abzielt, und UpdateUserAction (zum Zählen von Punkten usw.), die auf den Benutzeraggregatstamm abzielen. Diese werden asynchron über Punkt-zu-Punkt-Nachrichten gesendet.

Die aggregierten Wurzeln machen ihr Ding und wenn alles gut geht, feuern sie die Ereignisse QuestionUpdated und UserActionUpdated ab, die einen Status enthalten, der an einen Event Store ausgelagert ist. Yadayada wird beibehalten, nur um vollständig zu sein, hier geht es nicht wirklich darum.

Diese Ereignisse werden auch zur Übertragung in eine Pub / Sub-Warteschlange gestellt. Jeder Abonnent (darunter wahrscheinlich ein oder mehrere Projektoren, die die Leseansichten erstellen) kann diese Ereignisse abonnieren.

Die allgemeine Frage: Ist es in der Tat eine bewährte Methode, dass Befehle Punkt zu Punkt übertragen werden (dh der Empfänger ist bekannt), während Ereignisse gesendet werden (dh die Empfänger sind unbekannt)?

Unter den oben genannten Umständen, was wäre der Vor- / Nachteil, wenn Befehle anstelle von Punkt zu Punkt über Pub / Sub gesendet werden könnten?

Beispiel: Beim Senden von Befehlen während der Verwendung von Saga kann dies ein Problem sein, da die Vermittlungsrolle, die eine Saga bei einem Ausfall einer der aggregierten Wurzeln spielen muss, behindert wird, da die Saga zunächst nicht weiß, an welchen aggregierten Wurzeln sie beteiligt ist .

Andererseits sehe ich Vorteile (Flexibilität), wenn Sendebefehle erlaubt wären.

Geert-Jan
quelle
Gut geschriebene Frage übrigens.
Dav

Antworten:

17

Haftungsausschluss: Ich mache nur meine ersten Schritte in der CQRS-Welt, aber ich kann mein aktuelles Verständnis der Angelegenheit anbieten und wir werden sehen, ob andere dies bestätigen. Alles, was ich unten schreibe, hat ein zugrunde liegendes "wie ich es sehe" -Thema und ist nicht maßgebend.

Der 80% Fall

Um Ihre Frage zu beantworten, sind Befehle in der Tat eine Punkt-zu-Punkt-Angelegenheit. Wenn ein Befehl in einen Controller (MVC-Webanwendung) eingeht, fordert dieser Controller einen Befehls-Dispatcher auf, einen und nur einen geeigneten Befehlshandler zu finden, und delegiert die Arbeit an diesen Handler.

Warum nicht veröffentlichen?

Es ist eine Frage der Verantwortung . Wenn etwas einen Befehl sendet, bedeutet dies die Erwartung, dass er erfüllt wird. Wenn Sie einfach veröffentlichen und hoffen, dass irgendwo etwas es aufnimmt und darauf reagiert, gibt es keine Garantie dafür, dass dies der Fall ist. Durch Extrapolation wissen Sie auch nicht, ob sich mehrere Handler nicht für einen Befehl entscheiden, was möglicherweise dazu führt, dass dieselbe Änderung mehr als einmal angewendet wird.

Ereignisse hingegen sind informativer Natur, und es ist vernünftig zu erwarten, dass null, zwei oder mehr Komponenten an einem bestimmten Ereignis interessiert sind. Es ist uns nicht wirklich wichtig, die angeforderte Änderung vorzunehmen.

Beispiel

Dies könnte mit dem wirklichen Leben verglichen werden. Wenn Sie drei Kinder haben, gehen Sie in ein Zimmer und rufen Sie einfach "Reinigen Sie das Badezimmer". Sie haben keine Garantie dafür, dass es jemand tut, und vielleicht, wenn es nicht zweimal gemacht wird (wenn Sie gehorsame Kinder haben ;-) Das sollten Sie Es geht Ihnen besser, wenn Sie einem bestimmten Kind das zuweisen, was Sie tun möchten.

Wenn das Kind seine Arbeit beendet hat, ist es jedoch praktisch, wenn es "Badezimmer wurde gereinigt" ruft, damit jeder, der sich die Zähne putzen möchte, weiß, dass er dies jetzt tun kann.

Dav
quelle
macht sehr viel Sinn. Ausgezeichnete Analogie :)
Geert-Jan
Du hast mich verloren bei .. When a command enters a controller (MVC webapp)-? Verwenden Sie RESTful? oder einige hybride API-Endpunkte? Könnten Sie bitte ein Beispiel hinzufügen
Piotr Kula
@ppumkin Wir haben einen WebAPI-Endpunkt verwendet, der von unserer Web-App jedes Mal aufgerufen wird, wenn ein Befehl ausgeführt werden muss. Z.B. Wenn der Benutzer einen Post-Kommentar hinzufügen möchte, sendet die Web-App eine POST-Anfrage an example.com/api/Post/AddPostComment.
Dav
0

Ich bin damit einverstanden, dass ein initiierendes System im Allgemeinen niemals erwarten würde, dass ein Befehl von mehreren Zielsystemen ausgeführt wird:

  • Befehle werden normalerweise nie gesendet und gebetet - das initiierende System eines Befehls möchte im Allgemeinen eine asynchrone Rückmeldung, wenn der Fortschritt und das Ergebnis des Befehls stattfinden (z. B. Statusereignisse wie acknowledgementund erfolgreich completionoder failurekönnen vom Zielsystem veröffentlicht werden, um den initiierenden Benutzer zu informieren System).
  • Wenn mehrere Zielsysteme beteiligt wären, würde das initiierende System mehrere (möglicherweise widersprüchliche) Ergebnisse für den Befehl erhalten (z. B. Ziel 1 erfolgreich, Ziel 2 jedoch fehlgeschlagen). Dies würde zusätzliche Komplexität bei der Bestimmung des tatsächlichen Status des ursprünglichen Befehls erfordern (z. B. würde die Befehlstransaktion nur dann als erfolgreich angesehen, wenn alle Ziele erfolgreich waren? Müsste der Befehl bei erfolgreichen Zielen zurückgesetzt oder kompensiert werden, wenn eines der Ziele fehlschlug? etc.). Dies würde zu unerwünschter Kopplung und Komplexität zwischen dem initiierenden und dem Zielsystem führen.

Es ist jedoch immer noch sinnvoll, zusätzliche (nicht transaktionale und normalerweise recht "promiskuitive") Verbraucher zu abonnieren, die die zwischen Systemen ausgegebenen Befehle "belauschen"

  • Prüfungszwecke
  • Instrumentierungs- und Betriebsmetriken (z. B. Last, Geschäftsanalyse usw.)
  • Komplexe Ereignisverarbeitung oder Stream-Ereignisverarbeitung "Lauscher" - Diese Systeme können Fehler oder Unregelmäßigkeiten in den Befehlen erkennen (z. B. Häufigkeit von Befehlen oder Korrelation zwischen verschiedenen Kombinationen von Befehlen und Ereignissen) und Warnungen oder Korrekturmaßnahmen auslösen (häufig in der.) Form weiterer, verschiedener Arten von Befehlen).
StuartLC
quelle