2014-04-23 9 views
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.CQRS-Komponentenrollen und -Verantwortlichkeiten mit einer REST-API

Bis jetzt habe ich die folgenden Komponenten mit den damit verbundenen Verantwortlichkeiten (siehe unten). Ich habe einige Fragen in den folgenden Punkten angesprochen.

REST-Endpunkte/Anwendung

  • Anfrage Empfangen von Benutzer/ui/etc
  • Baukonstruktion und dem Versand relevanter Befehl
  • Wenn die Sollwerte von anderen begrenzten Kontexten erfordern, führen Sie dann die entsprechenden Finder Aufrufe erforderlich, um den Befehl korrekt zu instanziieren (z. B. Auftrag erfordert Benutzer-ID)
  • Im Falle eines 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 (anstelle der vollständigen DTOs) in die Befehle eingegeben werden, 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 Domain durch den Versand Befehle gemacht werden
  • Befehle sind unveränderlich und enthalten die notwendigen Daten für eine Bounded Context zu verändern, es Zustand ist oder werfen eine Ausnahme
  • Der Befehl Die Eingabe kann bei der Objekterzeugung überprüft werden, um zu vermeiden, dass ungültige Befehle gesendet werden.
  • Zum Beispiel: new CreateOrderCommand(orderId, userId, orderItems);

Befehl Handler

  • Der Handler kann entweder den Befehl erfolgreich anwenden oder eine Ausnahme
  • Es erheben kann nur ein Befehlshandler für jeden Befehl seines
  • Der Handler wird die Aggregate laden oder erstellen Root (Repository oder Aggregate Factory)
  • Der Handler wird den Befehl auf den Aggregatstamm anwenden
  • Der Handler-Deal s mit dem Repository
  • Keine Befehle auslösen (innerhalb oder außerhalb des beschränkten Kontexts)
  • Sollte der Befehls-Handler Ereignisse versenden? Zum Beispiel nach dem erfolgreichen Speichern in der Datenbank? Oder ist das allein die Verantwortung des Aggregates?

Aggregate Fabrik

  • Fasst die Logik erforderlich, um ein Aggregat Wurzel richtig
  • Die Fabrik zur Initialisierung kann auf das Repository zugreifen
  • Sollten die Fabrik Zugang Domain Services?
  • Zum Beispiel: factory.createOrder(orderId, userId, orderItems);

Aggregate Root/Aggregate

  • Enthält die Domänenlogik, Zustand und Verhalten
  • Verantwortlich für den Versand Events
  • Aggregate Root-Zugriff auf Aggregate kapselt
  • Aggregat Root sollte eine ID haben, die eindeutig i order.cancel();

Domain-Service

  • Dies beinhaltet, was passt nicht ganz in: dentifies es
  • Sollte nicht mit externen Diensten (ausgenommen Ereignisse Verlag)
  • Zum Beispiel interagieren ein Aggregatstamm
  • Mit welchen Komponenten kann der Domänenservice interagieren?
  • Sollte der Domain-Service Befehle/Ereignisse auslösen?
  • Zum Beispiel: Nicht sicher, was hier zu verwenden ist, der erste Punkt oben ist vage bestenfalls vage. Das meiste Verhalten liegt gut im Aggregat oder kann durch Sagas/Events/Commands erreicht werden. Was wäre ein gültiges Beispiel hier?

Repository

  • Kümmert Laden/Speichern/Aktualisieren/etc unsere Aggregate
  • Zum Beispiel: repo.load(orderId);

Ereignis

  • Stellt etwas, das in einem Aggregat hat (oder Befehls-Handler, etc., wenn sie auch Ereignisse feuern)
  • Events unveränderlich sind
  • Andere Kontexte im System begrenzt ein Ereignis verwenden können Entscheidungen
  • Zum Beispiel zu machen : new OrderCancelledEvent(orderId);

Event Handler

  • Reagiert auf ein Ereignis tha t fand
  • Es können mehrere Event-Handler für ein einzelnes Ereignis in der gleichen oder unterschiedlichen begrenzten Kontexten
  • kann mit dem Infrastruktur-Services interagieren: OrderCancelled => OrderCancelledHandler => EmailService.sendEmail()
  • neue Befehle feuern
  • 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, der über die gleichen oder mehr begrenzten Kontexte (Koordinaten) sitzt
  • empfangen Ereignisse
  • Behält den Zustand einer Kette/Satz von Ereignisse
  • Normalerweise wird der Status beibehalten
  • Timeouts können eingestellt werden, um Status zu überprüfen/zu ändern (kann Vorstellung von Zeit haben)
  • Staaten können Nebenwirkungen haben, wie: Befehle abfeuern, mit Finders sprechen, mit den Infrastrukturdiensten interagieren (z.B.E-Mail)
  • Zum Beispiel: Warten Sie OrderShipped und orderreceived Ereignisse => Feuer CancelOrderCommand Warten Sie OrderCancelled => Feuer Bestellung storniert E-Mail

Finder

  • verwendet, um eine readmodel von abrufen der Kontext (e)
  • Liefert im Allgemeinen Datenobjekte vom Typ Objekt (DTO)
  • Fin der sollte nicht in der Schreibseite unserer Anwendung (weniger Kopplung)
  • Einzel (read + write) normalisierte Datenbankmodell zu finden: der Finder andere Finders aufrufen können (über Kontexte) verschachtelten Objekten zu erfüllen
  • Lesen Spezifische De- normalisierte Datenbankmodell: der Finder die Daten alle in einer Datenbank Aufruf
  • Zum Beispiel erhalten: finder.findOrdersOnDate(date);

Infrastructure Services

  • Angebote mit Infrastruktur: DB-Zugriff, E-Mails, Nachrichtenwarteschlangen usw.

Frage

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

+1

["Implementierung von domänengesteuertem Design"] (https://vaughnvernon.co/?page_id=168) von Vaughn Vernon bietet technische Details, die jede dieser Komponenten abdecken. Ich würde es als eine sehr nützliche Ressource zusätzlich zu Eric Evans Domain-Driven Design empfehlen. –

+0

Zufälligerweise fand ich letzte Nacht einen Artikel von Vaughn Vernon. Sehr interessant! Ich werde das Buch kaufen. Was würdest du der Liste basierend auf dem Buch hinzufügen? –

Antwort

3

Wie Sie sagten, gibt es viele Meinungen da draußen, und Sie müssen sie filtern, da die Leute die meiste Zeit Meinungen ohne Erfahrung in der Sache geben. 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 empfehlen Ihnen, so starten Sie den CQRS Teil Ihrer Architekturgebäude, ein Gateway für die Befehle und eine für die Abfragen, weil das gemeinsam ist und auf nur, dass es so viele Entscheidungen getroffen werden:

  • Erholung API

  • Nachricht Verträge/Validierung

  • lesen Modelle ...

und beginnen Sie, Ihren Dienst auf traditionelle Weise ohne DDD zu implementieren, indem Sie einfach 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 Ratschlag wäre nicht zu versuchen, alles auf einmal zu tun, weil Sie scheitern werden; Ich habe es schon oft gesehen.

Zum Beispiel: Warten Sie OrderShipped und orderreceived Ereignisse => Feuer CancelOrderCommand Warten OrderCancelled => Feuer Bestellung storniert E-Mail

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

Einzel normalisierte Datenbankmodell (+ Schreib lesen): Der Finder kann andere Finders aufrufen (über Kontexte) verschachtelten Objekten zu erfüllen

Welche anderen Kontexten Sie in Ihre Lese Modelle haben?

Infrastructure Services

Deals with infrastructure: db access, send emails, message queues, etc 

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

+0

Hallo Marco, danke für die Antwort. Ich stimme der Aufteilung in Phasen zu. Ich habe das System bereits überarbeitet, um mehr DDD-orientiert zu sein. Die Interaktion zwischen Kontexten wurde jedoch immer mit direktem Aufruf der Fassade eines anderen Kontextes durchgeführt. Dies verbindet offensichtlich alles eng miteinander. Mit dem CQRS-Ansatz wird die Entkopplung viel einfacher. Die Hauptsache ist, wie Sie jetzt die Kommunikation zwischen Kontexten koordinieren: d. H. Mit Hilfe von Ereignissen und Sagas (wo nötig). –

+0

Lassen Sie mich die drei Extrakte beantworten, die Sie aus der Liste gezogen haben: 1) Die Saga veröffentlicht keine Ereignisse, sie wartet auf Ereignisse und löst dann Befehle aus oder interagiert mit der Infrastruktur. –

+0

2) Normalisierte DB: Zum Beispiel könnte ich einen Bildschirm haben, der einen Benutzer und Informationen über Interaktionen hat, die der Benutzer mit dem System gehabt hat. Anstatt zwei Anrufe zu tätigen und sie zusammenzustellen, würde ich erwarten, einen einzelnen Finder aufzurufen, der die Daten zurückgibt, die ich für diesen Bildschirm brauche. Dies ist mit einem denormalisierten Modell einfach, aber für normalisierte Daten können die Daten in verschiedenen Kontexten verteilt werden, was bedeutet, dass ich einen anderen Finder verwenden muss (wenn es verschiedene Aggregate im selben Kontext wären, könnte ich einfach das relevante Repository verwenden). –