Wie erstelle ich einen neuen Aggregatstamm in CQRS?

10

Wie sollen wir neue aggregierte Wurzeln in der cqrs-Architektur erstellen? In diesem Beispiel möchte ich ein neues aggregiertes Stamm-AR2 erstellen, das auf das erste AR1 verweist.

Ich erstelle AR2 mit der AR1-Methode als Ausgangspunkt. Bisher sehe ich nur wenige Möglichkeiten:

  1. Innerhalb der Methode in AR1 createAr2RootOpt1konnte ich new AR2()dieses Objekt sofort mithilfe eines Domänendienstes mit Zugriff auf das Repository aufrufen und in der Datenbank speichern.
  2. Ich könnte ein Ereignis in der ersten aggregierten Wurzel ausgeben, z. SholdCreateAR2Eventund dann eine zustandslose Saga haben, die darauf reagiert und einen Befehl ausgibt, CreateAR2Commandder dann verarbeitet wird und tatsächlich AR2 erstellt und ausgibt AR2CreatedEvent. Bei Verwendung der Ereignisbeschaffung wird SholdCreateAR2Eventdiese nicht im Ereignisspeicher beibehalten, da sie den Status des ersten aggregierten Stamms nicht beeinflusst. (Oder sollten wir dies trotzdem im Event Store speichern?)

    class AR1{
        Integer id;
        DomainService ds;
    
        //OPTION 1
        void createAr2RootOpt1(){
            AR2 ar2 = new AR2();
            ds.saveToRepo(ar2);
        }
    
        //OPTION 2
        void createAr2RootOpt2(){
            publishEvent(new SholdCreateAR2Event());    //we don't need this event. Shoud it still be preserved in event store?
        }
    }
    
    class AR2{
        Integer id;
        Integer ar1Id;
    
        void handle(CreateAR2Command command){
            //init this AR with values and save
            publishEvent(AR2CreatedEvent());    //used for projections afterwards and saved inside AR2 event store
        }
    }
    
    class Saga{
        void handle(SholdCreateAR2Event ev){
            emitCommand(new CreateAR2Command());
        }
    }
    

Was ist der richtige Weg, dies zu tun?

Bojan Vukasovic
quelle

Antworten:

2

Ich denke, diese Option nein. 2 ist die Lösung mit einer kleinen, aber wichtigen Änderung: Es AR1sollte kein Ereignis ausgegeben werden, dessen Zweck es ist, das zu erstellen AR2, sondern es sollte ein AR1WasCreatedEreignis ausgegeben werden . Dieses Ereignis sollte im Ereignisspeicher beibehalten werden, da es ein wichtiges Ereignis ist, das die Geburt von markiert AR1. Dann sollte ein SagaEreignis aufgelistet AR1WasCreatedund ein Befehl zum Erstellen generiert werden AR2: CreateAR2Command.

Option Nr. 1 ist sehr falsch. Sie sollten diese Art von Domain-Service niemals in eine Aggregate. Aggregatessollte rein sein, ohne andere Nebenwirkungen als die Erzeugung von Ereignissen.

PS Ich sende niemals Ereignisse vom Konstruktor von aus, Aggregateda zwischen dem Erstellen einer Instanz eines Objekts (im Sinne der Programmiersprache) und dem Erstellen (der Geburt, wenn Sie möchten) eines Objekts unterschieden wird Aggregate. Ich emittiere Ereignisse nur von einer handleMethode (bei der Behandlung von a command).

Constantin Galbenu
quelle
Was meinst du damit AR1WasCreated? Sollte es sein AR2WasCreated? Wenn ich Ihre Logik verwende, gebe ich ein Ereignis aus, AR2WasCreatedbevor es tatsächlich erstellt wird. Das Speichern dieses Ereignisses im Ereignisprotokoll von AR1 scheint problematisch zu sein, da ich diese Daten in AR1 nicht benötige (es ändert nichts in AR1).
Bojan Vukasovic
OK, 3 Jahre später. Es geht AR1WasCreated-> SAGA (hat Regel, wenn A1 erstellt wurde, dann erstellen Sie A2) -> CreateAR2Command-> AR2WasCreated.
Bojan Vukasovic
@BojanVukasovic Ich bin froh, dass es so funktioniert hat, wie ich es geschrieben habe :)
Constantin Galbenu
2

Wie sollen wir neue aggregierte Wurzeln in der cqrs-Architektur erstellen?

Schöpfungsmuster sind komisch .

Udi Dahan hat einige nützliche Dinge über das allgemeine Problem zu sagen: Erstellen Sie keine aggregierten Wurzeln . Der grundlegende Punkt ist, dass Aggregate nicht einfach aus dem Nichts herausspringen und dass es eine Domänensprache gibt, die beschreibt, wie sie angezeigt werden und die in Ihrem Domänenmodell erfasst werden sollte.

Es besteht die Tendenz, dass die Entität in Ihrem Domänenmodell, die den Befehl verarbeitet, nicht die Entität ist, die durch die Transaktion geändert wird. Das ist nicht falsch; Es ist einfach komisch (im Vergleich zu den Fällen, in denen Sie eine Entität bitten, sich selbst zu ändern.

Ihr zweiter Ansatz ist ebenfalls in Ordnung. "Ereignisse, die wir auslösen, ohne sie tatsächlich in der Datenbank zu speichern" werden manchmal als "Domänenereignisse" bezeichnet.

Die Grundidee besteht darin, dass der Befehlshandler innerhalb derselben Transaktion das Ereignis auslöst, das entlang des Busses zu einem Ereignishandler geleitet wird, der es dem zweiten Aggregat ermöglicht, sich selbst zu erstellen. Vielleicht erhalten Sie einen etwas besseren Code-Zusammenhalt.

Hinweis: In Systemen mit Ereignisquellen verwenden Sie Ereignisse normalerweise nicht auf diese Weise.

Bei Verwendung der Ereignisbeschaffung sollte ShouldCreateAR2Event nicht im Ereignisspeicher beibehalten werden, da dies den Status des ersten aggregierten Stamms nicht beeinflusst.

Hinweis: Ereignisnamen sind normalerweise in der Vergangenheitsform - ShouldCrateAR2 hat die falsche Schreibweise.

Ja, wenn Sie nur ein Ereignis auf den Synchronbus werfen, um Remotecode auszuführen, sollten Sie dieses Ereignis nicht im Buch der Aufzeichnung speichern. Es ist nur ein Implementierungsdetail in dieser Größenordnung.

Oder sollten wir dies trotzdem im Event Store speichern?

Vermeiden Sie es, zwei verschiedene Ereignisströme in derselben Transaktion zu ändern. Wenn diese Erstellung auch eine Änderung an AR1 darstellt, besteht die übliche Antwort darin, AR1 in dieser Transaktion mit einem asynchronen Abonnenten für die Ereignisse zu ändern, die für das Auslösen des Befehls zum Erstellen von AR2 verantwortlich sind.

Idempotente Befehlsbehandlung hilft hier sehr.

VoiceOfUnreason
quelle
danke für die Antwort. Eine weitere Sache, die nicht 100% klar ist - später, wenn ich AR2 als Argument für AR1 verwenden muss, wie soll ich das übergeben -, da CQRS angibt, dass AR nur zum Schreiben und nicht zum Abfragen verwendet werden soll. Ich habe jedoch keine andere Option als die Verwendung, AR1.doSmthn(AR2 param)da jede von mir erstellte Leseprojektion keine vollständigen Daten enthält, die ich benötige (nur AR2 verfügt über vollständige Daten).
Bojan Vukasovic
> "Ja, wenn Sie nur ein Ereignis auf den Synchronbus werfen, um Remotecode auszuführen, sollten Sie dieses Ereignis nicht im Buch der Aufzeichnung speichern." Ich denke, das Speichern hat einen echten Wert, da Sie wissen, dass der Prozess gestartet wurde, damit etwas passiert. Sie können jetzt auch verfolgen, ob dies tatsächlich abgeschlossen ist. Aber ich denke, es hängt vom Anwendungsfall ab
Chaosekie