Wie gehe ich mit Nebenwirkungen in CRQS um, wenn Ereignisse wiedergegeben werden?

10

Es wird gesagt, dass es in CQRS einfach ist, einen Fehler zu beheben. Sie müssen die Ereignisse nur erneut bereitstellen und dann wiedergeben.

Was ist jedoch, wenn eines der Ereignisse dazu führen sollte, dass ein externes System, das nicht unter Ihrer Kontrolle steht, einen Artikel an den Kunden versendet, wenn Sie nur die Ereignisse wiederholen, bei denen der Artikel zweimal versendet wird?

Wie lösen Sie das?

Jas
quelle

Antworten:

6

Sie müssen klar zwischen Ereignissen unterscheiden, die den Status Ihres Lesemodells ändern, und Ereignissen, die (möglicherweise) den Status externer Systeme ändern. Stellen Sie sicher, dass Sie keine "gemischten Ereignisse" haben, die beide Zustände zusammen ändern. Auf diese Weise können Sie Ihre Ereignisse in einem bestimmten "Wiedergabemodus" wiedergeben, in dem diese Ereignisse für das externe System nicht erneut ausgelöst werden. In diesem Modus "simulieren" Sie auch alle Ereignisse, die ursprünglich vom externen System initiiert wurden (jetzt nehmen Sie sie stattdessen aus der Wiedergabewarteschlange).

Vergessen Sie nicht, dass der Schritt zum erneuten Bereitstellen tatsächlich bedeutet, den Status des Lesemodells auf einen früheren Zeitpunkt zurückzusetzen. Dies ist wahrscheinlich nichts, was Sie für den Status der externen Systeme tun können (oder müssen).

Doc Brown
quelle
"Vergessen Sie nicht, dass der Schritt zum erneuten Bereitstellen tatsächlich bedeutet, den Status des Lesemodells auf einen früheren Zeitpunkt zurückzusetzen. Dies ist wahrscheinlich nichts, was Sie für den Status der externen Systeme tun können (oder müssen)." -> aber was ist, wenn ich möchte, dass meine Wiedergabe fehlgeschlagene externe Systemaufrufe wie Versand erneut versucht? In diesem Fall würde meine erneute Bereitstellung einer Wiederholung nicht nur den Status des Lesemodells zurücksetzen, sondern auch externe Ereignisse verursachen. Klingt dies fair oder fehlt mir etwas?
Jas
2
@Jas: Sie möchten "Wiederholung" nicht missbrauchen, um einen fehlgeschlagenen externen Systemaufruf erneut zu versuchen. Sie verwenden die "Wiedergabe", um das Lesemodell Ihres eigenen Systems in den Zustand zu versetzen, in dem es zuvor war. Das heißt, im Falle einer fehlgeschlagenen Versandanfrage wurde Ihr System zuvor über diesen Fehler informiert und diese Informationen irgendwo in seinem Status gespeichert. Die Wiedergabe stellt sicher, dass diese Informationen nach "Neu bereitstellen und Wiedergeben" noch vorhanden sind. Nach der Wiedergabe wendet Ihr System möglicherweise eine Strategie zum erneuten Versand im Fehlerfall an (die nichts mit CQRS zu tun hat. Jedes robuste Bestellsystem sollte einfach eine solche Strategie haben).
Doc Brown
Interessant, das war es, was ich vorhatte, ich habe mich nur gefragt, ob es ein "Muster" gibt, damit ich das Rad nicht neu erfinde!
Jas
3

Aus Martin Fowlers Event-Sourcing- Artikel:

Die Grundidee von Event Sourcing besteht darin, sicherzustellen, dass jede Änderung des Status einer Anwendung in einem Ereignisobjekt erfasst wird und dass diese Ereignisobjekte selbst in der Reihenfolge gespeichert werden, in der sie für dieselbe Lebensdauer wie der Anwendungsstatus selbst angewendet wurden.

Wenn Sie also den Status Ihres Systems zu einem bestimmten Zeitpunkt wiederherstellen müssen, spielen Sie den gespeicherten Status und nicht die Ereignishandler bis zu diesem Moment erneut ab.

Wenn Sie jedoch nur mit Statusdaten arbeiten, sollte dies keine Auswirkungen auf das externe System haben. Es sei denn, Sie haben Trigger oder Beobachter in Ihrem Ereignisspeicher. In diesem Fall sollten Sie diese für die Dauer der Wiederherstellung deaktivieren. Da Sie sagen, dass Sie keine Kontrolle über das externe System haben, sollten Sie nicht versuchen, seinen Status mithilfe der exponierten API wiederherzustellen, da Sie nicht wissen, welche Nebenwirkungen es in ihrem System haben kann. Wenn die Wiederherstellung das System in einen Zwischenzustand versetzt (z. B. aufgrund fehlgeschlagener Vorgänge im externen System), sollte dies nicht in die Zuständigkeit einer Ereigniswiedergabe fallen.

devnull
quelle
2

Was ist jedoch, wenn eines der Ereignisse dazu führen sollte, dass ein externes System, das nicht unter Ihrer Kontrolle steht, einen Artikel an den Kunden versendet, wenn Sie nur die Ereignisse wiederholen, bei denen der Artikel zweimal versendet wird?

Um ein bestimmtes Beispiel auszuwählen, betrachten wir, wie ein "mindestens einmaliger" Ansatz für Nebenwirkungen funktionieren könnte.

State currentState = State.InitialState
for(Event e : events) {
    currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
    performSideEffect(s)

Das Domänenmodell verfolgt also, was zu tun ist. überlässt aber das eigentliche Tun der Anwendung

Im Zusammenhang mit der Ausführung eines Befehls sieht die Grundidee gleich aus. Die tatsächlichen Nebenwirkungen treten außerhalb der Transaktion auf, die das Modell aktualisiert.

Die Unit-Tests für Ihr Modell könnten also ungefähr so ​​aussehen

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced)

    // Then
    List.of(SendEmail) === currentState.applyAll(events).querySideEffects()
}

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced, EmailSent)

    // Then
    List.EMPTY === currentState.applyAll(events).querySideEffects()
}

Die Hauptpunkte hier sind

  • Das Aktualisieren des Modells ist nebenwirkungsfrei. Die tatsächlichen Nebenwirkungen treten außerhalb der Transaktion auf, die das Modell aktualisiert.
  • Ein Ereignis, das das Ergebnis der Nebenwirkung beschreibt, muss zurückkommen.
VoiceOfUnreason
quelle