CQRS-Komponentenrollen und -Verantwortlichkeiten mit einer REST-API

8

Es gibt eine Menge Meinungen bezüglich CQRS mit DDD und was jede Komponente ausmacht. Ich habe noch nicht damit begonnen, sich mit Event Sourcing zu beschäftigen, daher enthält die folgende Liste nichts, was damit zu tun hat. Obwohl Einblicke in ES wäre interessant.

Bisher habe ich die folgenden Komponenten mit zugehörigen Verantwortlichkeiten (siehe unten). Ich habe einige Fragen in die unten stehenden Punkte eingefügt.

REST-Endpunkte / Anwendung

  • Erhalte eine Anfrage von user / ui / etc
  • Konstruieren und senden Sie den relevanten Befehl
  • Wenn für den Befehl Werte aus anderen beschränkten Kontexten erforderlich sind, führen Sie die relevanten Finder-Aufrufe durch, die zum korrekten Instantiieren des Befehls erforderlich sind (z. B. Reihenfolge erfordert Benutzer-ID)
  • Bei einem GET: Der entsprechende Finder heißt
  • Finder sitzen auf dieser Ebene der Anwendung. Die schreibgeschützte Seite von Bounded Contexts (Befehlshandler, Aggregat, Factory, Domänenservice usw.) sollte Finder nicht aufrufen. Dadurch wird die Einkapselung aufrechterhalten, und indem nur die erforderlichen Daten in die Befehle eingegeben werden (anstelle von vollständigen DTOs), wird es zu einer bescheidenen Anti-Korruptionsschicht.
  • Zum Beispiel:

AggregateId orderId = AggregateId.get (); AggregateId userId = finder.findUserAggregateIdByEmail (E-Mail); dispatcher.fire (neuer CreateOrderCommand (orderId, userId, orderItems));

Befehl

  • Änderungen an der Domäne werden durch das Senden von Befehlen
  • vorgenommen
  • Befehle sind unveränderlich und enthalten die Daten, die für einen beschränkten Kontext erforderlich sind, um seinen Status zu ändern oder eine Ausnahme auszulösen
  • Die Befehlseingabe kann bei der Objekterstellung validiert werden, um zu vermeiden, dass ungültige Befehle gesendet werden
  • Zum Beispiel: new CreateOrderCommand( orderId, userId, orderItems );

Befehlshandler

  • Der Handler kann den Befehl entweder erfolgreich anwenden oder eine Ausnahme auslösen
  • Es kann nur einen Befehlshandler für jeden Befehl geben
  • Der Handler lädt oder erstellt den Gesamtstamm (Repository oder Aggregate Factory)
  • Der Handler wird den Befehl auf das aggregierte root
  • anwenden
  • Der Handler behandelt das Repository
  • Befehle sollten nicht ausgelöst werden (innerhalb oder außerhalb des beschränkten Kontextes)
  • Soll der Befehlshandler Ereignisse senden? Zum Beispiel nach dem erfolgreichen Speichern in der Datenbank? Oder ist das allein die Verantwortung des Aggregates?

Aggregatfabrik

  • Kapselt die Logik, die erforderlich ist, um einen Aggregatstamm richtig zu initialisieren
  • Die Fabrik kann auf das Repository zugreifen
  • Sollte die Factory auf Domain Services zugreifen?
  • Zum Beispiel: factory.createOrder( orderId, userId, orderItems );

Aggregatwurzel / Aggregate

  • Enthält die Domänenlogik, den Status und das Verhalten
  • Verantwortlich für das Absenden von Events
  • Aggregate Root kapselt den Zugriff auf Aggregates
  • Aggregatwurzel sollte eine ID haben, die es eindeutig identifiziert
  • Sollte nicht mit externen Services interagieren (außer einem Event-Publisher)
  • Zum Beispiel: order.cancel();

Domänenservice

  • Dies enthält, was nicht ganz in eine Aggregatwurzel passt
  • Mit welchen Komponenten kann der Domain-Service interagieren?
  • Soll der Domänenservice Befehle / Ereignisse auslösen?
  • Zum Beispiel: Ich bin mir nicht sicher, was ich hier verwenden soll, der erste Punkt oben ist bestenfalls vage. Das meiste Verhalten liegt gut im Aggregat oder kann durch Sagas / Events / Commands erreicht werden. Was wäre hier ein gültiges Beispiel?

Repository

  • kümmert sich um das Laden / Speichern / Aktualisieren / usw. unserer Aggregate
  • Zum Beispiel: repo.load(orderId);

Ereignis

  • Repräsentiert etwas, das in einem Aggregat (oder Befehls-Handler usw.) stattgefunden hat, wenn auch Ereignisse ausgelöst werden können.
  • Ereignisse sind unveränderlich
  • Andere beschränkte Kontexte im System können ein Ereignis verwenden, um Entscheidungen zu treffen
  • Zum Beispiel: new OrderCancelledEvent( orderId );

Ereignishandler

  • Reagiert auf ein Ereignis, das stattgefunden hat
  • Es kann mehrere Ereignisbehandlungsroutinen für ein einzelnes Ereignis im selben oder in verschiedenen beschränkten Kontexten geben
  • Kann mit Infrastrukturdiensten interagieren: OrderCancelled = & gt; OrderCancelledHandler = & gt; EmailService.sendEmail ()
  • Kann neue Befehle auslösen
  • Kann mit Finders sprechen
  • Wenn der Event-Handler Befehle absetzt, mit Finders spricht und mit der Infrastruktur interagiert, ähnelt er dem Saga-Verhalten (oder dem REST-Enpoint-Verhalten). Außer es ist die Reaktion auf ein einzelnes Ereignis und nicht auf eine Kette / einen Satz von Ereignissen.

Saga

  • Behält einen Geschäftsprozess bei, der sich über denselben oder mehrere beschränkte Kontexte (Koordinaten) befindet
  • Empfängt Ereignisse
  • Behält den Zustand einer Kette / Menge von Ereignissen bei
  • Normalerweise wird der Status beibehalten
  • Timeouts können gesetzt werden, um den Status zu prüfen / zu ändern (kann eine Vorstellung von Zeit haben)
  • Staaten können Nebenwirkungen haben, wie z. B. Befehle absetzen, mit Finders sprechen, mit den Infrastrukturdiensten (z. B. E-Mail) interagieren
  • Zum Beispiel: Warte auf OrderShiped und OrderReceived events = & gt; fire CancelOrderCommand Warte auf OrderCancelled = & gt; Feuerauftrag stornierte E-Mail

Finder

  • Wird verwendet, um ein Readmodel des Kontexts (der Kontexte) abzurufen
  • Liefert in der Regel Datenobjekte vom Typ Data Transfer Object (DTO)
  • Finder sollte nicht auf der Schreibseite unserer Anwendung gefunden werden (weniger Kopplung)
  • Single (Lesen + Schreiben) Normalisiertes Datenbankmodell: Der Finder kann andere Finder (über Kontexte) aufrufen, um verschachtelte Objekte zu erfüllen
  • Lesen Sie das spezifische de-normalisierte Datenbankmodell: Der Finder erhält die Daten in einem Datenbankaufruf
  • Zum Beispiel: finder.findOrdersOnDate( date );

Infrastrukturdienste

  • Angebote für die Infrastruktur: Datenbankzugriff, E-Mails senden, Nachrichtenwarteschlangen usw.

Frage

Ist das eine genaue Zusammenfassung der Komponenten vs. Verantwortlichkeiten? Was fehlt und was sollte bewegt werden? Ich kann die Liste mit relevanten Antworten aktualisieren.

    
Alessandro Giannone 23.04.2014, 04:20
quelle

1 Antwort

3

Wie Sie sagten, gibt es viele Meinungen, und Sie müssen sie filtern, da die Leute meistens Meinungen abgeben, ohne Erfahrung in dieser Angelegenheit. CQRS ist ein großes Thema und ich denke nicht, dass Sie ohne vorherige Erfahrung in DDD und ES einsteigen sollten. Dienste sollten gut und klar definiert sein. Wenn Sie diesen Prinzipien folgen, können Sie verschiedene Implementierungen in Ihrer Domäne haben. Beginnen Sie also mit CQRS und fügen Sie DDD / ES den folgenden Diensten hinzu, sobald Sie CQRS beherrschen.

Ich würde Ihnen raten, den CQRS-Teil Ihrer Architektur zu erstellen, ein Gateway für die Befehle und eines für die Abfragen, weil das üblich ist und nur so viele Entscheidungen getroffen werden müssen:

  • Rest-API

  • Nachrichtenverträge / Validierung

  • Lesen Sie Modelle ...

und beginnen Sie, Ihren Dienst auf traditionelle Weise ohne DDD zu implementieren, indem Sie nur das Repository-Muster verwenden. Wenn Sie anfangen, sich sicher zu fühlen, dann können Sie vielleicht DDD in Bezug auf Aggregate und später auf ES springen. Sie können die anfänglichen Dienste jederzeit zu einem späteren Zeitpunkt ändern.

Mein Rat wäre, nicht zu versuchen, alles auf einmal zu tun, weil Sie versagen werden; Ich habe es schon oft gesehen.

  

Zum Beispiel: Warten auf OrderShipped und OrderReceived events = & gt; fire CancelOrderCommand Warte auf OrderCancelled = & gt; Feuerauftrag stornierte E-Mail

Sagas sollte keine Ereignisse (Saga-Muster) veröffentlichen, aggregierte Ereignisse sagen und Befehle senden. Die Tatsache, dass Frameworks wie NServiceBus es Sagas erlauben, Ereignisse zu veröffentlichen, hilft nicht, also sei dir bewusst.

  

Single (Lesen + Schreiben) Normalisiertes Datenbankmodell: Der Finder kann andere Finder (über Kontexte) aufrufen, um verschachtelte Objekte zu erfüllen

Welche anderen Kontexte möchten Sie in Ihren Lesemodellen haben?

  

Infrastrukturdienste

%Vor%

Nicht sicher, was Sie damit meinen, aber es sieht nicht richtig aus. Nachrichtenwarteschlange oder Datenbankdienst?

    
Marco 23.04.2014 11:02
quelle