Soweit ich weiß, besteht die große Idee hinter CQRS darin, zwei verschiedene Datenmodelle für die Verarbeitung von Befehlen und Abfragen zu haben. Diese werden als "Schreibmodell" und "Lesemodell" bezeichnet.
Betrachten wir ein Beispiel für den Klon einer Twitter-Anwendung. Hier sind die Befehle:
- Benutzer können sich registrieren.
CreateUserCommand(string username)
emittiertUserCreatedEvent
- Benutzer können anderen Benutzern folgen.
FollowUserCommand(int userAId, int userBId)
emittiertUserFollowedEvent
- Benutzer können Beiträge erstellen.
CreatePostCommand(int userId, string text)
emittiertPostCreatedEvent
Während ich oben den Begriff "Ereignis" verwende, meine ich nicht "Ereignisbeschaffungsereignisse". Ich meine nur Signale, die Lesemodellaktualisierungen auslösen. Ich habe keinen Event Store und möchte mich bisher auf CQRS selbst konzentrieren.
Und hier sind die Fragen:
- Ein Benutzer muss die Liste seiner Beiträge sehen.
GetPostsQuery(int userId)
- Ein Benutzer muss die Liste seiner Follower sehen.
GetFollowersQuery(int userId)
- Ein Benutzer muss die Liste der folgenden Benutzer anzeigen.
GetFollowedUsersQuery(int userId)
- Ein Benutzer muss den "Freund-Feed" sehen - ein Protokoll aller Aktivitäten seiner Freunde ("Ihr Freund John hat gerade einen neuen Beitrag erstellt").
GetFriedFeedRecordsQuery(int userId)
Um damit umgehen zu können, muss CreateUserCommand
ich wissen, ob ein solcher Benutzer bereits existiert. An diesem Punkt weiß ich also, dass mein Schreibmodell eine Liste aller Benutzer haben sollte.
Um damit umgehen zu können, muss FollowUserCommand
ich wissen, ob BenutzerA bereits BenutzerB folgt oder nicht. An dieser Stelle möchte ich, dass mein Schreibmodell eine Liste aller Benutzer-folgt-Benutzer-Verbindungen enthält.
Und schließlich CreatePostCommand
glaube ich nicht, dass ich etwas anderes brauche, weil ich keine Befehle wie habe UpdatePostCommand
. Wenn ich diese hätte, müsste ich sicherstellen, dass der Beitrag existiert, also würde ich eine Liste aller Beiträge benötigen. Da ich diese Anforderung nicht habe, muss ich nicht alle Beiträge verfolgen.
Frage 1 : Ist es tatsächlich richtig, den Begriff "Schreibmodell" so zu verwenden, wie ich ihn verwende? Oder steht "Schreibmodell" bei ES immer für "Event Store"? Wenn ja, gibt es eine Trennung zwischen den Daten, die ich für Befehle benötige, und den Daten, die ich für Abfragen benötige?
Um damit fertig zu werden GetPostsQuery
, würde ich eine Liste aller Beiträge benötigen. Dies bedeutet, dass mein Lesemodell eine Liste aller Beiträge enthalten sollte. Ich werde dieses Modell beibehalten, indem ich es mir anhöre PostCreatedEvent
.
Um beides GetFollowersQuery
und zu handhaben GetFollowedUsersQuery
, würde ich eine Liste aller Verbindungen zwischen Benutzern benötigen. Um dieses Modell beizubehalten, werde ich es mir anhören UserFollowedEvent
. Hier ist eine Frage Nr. 2 : Ist es praktisch in Ordnung, wenn ich hier die Verbindungsliste des Schreibmodells verwende? Oder sollte ich besser ein separates Lesemodell erstellen, da ich es in Zukunft möglicherweise mehr Details benötigen muss als das Schreibmodell?
Um damit fertig zu werden, GetFriendFeedRecordsQuery
müsste ich:
- Zuhören
UserFollowedEvent
- Zuhören
PostCreatedEvent
- Wissen, welche Benutzer welchen anderen Benutzern folgen
Wenn Benutzer A Benutzer B folgt und Benutzer B Benutzer C folgt, sollten die folgenden Datensätze angezeigt werden:
- Für Benutzer A: "Ihr Freund Benutzer B hat gerade begonnen, Benutzer C zu folgen."
- Für Benutzer B: "Sie haben gerade begonnen, Benutzer C zu folgen."
- Für Benutzer C: "Benutzer B folgt Ihnen jetzt"
Hier ist die Frage Nr. 3 : Welches Modell soll ich verwenden, um die Liste der Verbindungen zu erhalten? Soll ich ein Schreibmodell verwenden? Soll ich das Lesemodell verwenden - GetFollowersQuery
/ GetFollowedUsersQuery
? Oder sollte ich das GetFriendFeedRecordsQuery
Modell selbst dazu bringen, das zu handhaben UserFollowedEvent
und eine eigene Liste aller Verbindungen zu führen?