Pour autant que je sache, la grande idée derrière CQRS est d'avoir 2 modèles de données différents pour gérer les commandes et les requêtes. Ceux-ci sont appelés "modèle d'écriture" et "modèle de lecture".
Prenons un exemple de clone d'application Twitter. Voici les commandes:
- Les utilisateurs peuvent s'inscrire.
CreateUserCommand(string username)
émetUserCreatedEvent
- Les utilisateurs peuvent suivre les autres utilisateurs.
FollowUserCommand(int userAId, int userBId)
émetUserFollowedEvent
- Les utilisateurs peuvent créer des publications.
CreatePostCommand(int userId, string text)
émetPostCreatedEvent
Bien que j'utilise le terme «événement» ci-dessus, je ne parle pas des événements de «sourçage d'événements». Je veux juste dire des signaux qui déclenchent des mises à jour du modèle de lecture. Je n'ai pas de magasin d'événements et jusqu'à présent, je veux me concentrer sur le CQRS lui-même.
Et voici les requêtes:
- Un utilisateur doit voir la liste de ses publications.
GetPostsQuery(int userId)
- Un utilisateur doit voir la liste de ses abonnés.
GetFollowersQuery(int userId)
- Un utilisateur doit voir la liste des utilisateurs qu'il suit.
GetFollowedUsersQuery(int userId)
- Un utilisateur a besoin de voir le "flux d'amis" - un journal de toutes les activités de ses amis ("votre ami John vient de créer un nouveau message").
GetFriedFeedRecordsQuery(int userId)
Pour gérer, CreateUserCommand
j'ai besoin de savoir si un tel utilisateur existe déjà. Donc, à ce stade, je sais que mon modèle d'écriture devrait avoir une liste de tous les utilisateurs.
Pour gérer, FollowUserCommand
j'ai besoin de savoir si userA suit déjà userB ou non. À ce stade, je veux que mon modèle d'écriture ait une liste de toutes les connexions utilisateur-utilisateur-utilisateur.
Et enfin, pour gérer, CreatePostCommand
je ne pense pas avoir besoin d'autre chose, car je n'ai pas de commandes comme UpdatePostCommand
. Si je les avais, je devrais m'assurer que le message existe, donc j'aurais besoin d'une liste de tous les messages. Mais parce que je n'ai pas cette exigence, je n'ai pas besoin de suivre tous les messages.
Question n ° 1 : est-il vraiment correct d'utiliser le terme "modèle d'écriture" comme je l'utilise? Ou "écrire le modèle" signifie-t-il toujours "magasin d'événements" en cas d'ES? Dans l'affirmative, existe-t-il une sorte de séparation entre les données dont j'ai besoin pour gérer les commandes et les données dont j'ai besoin pour gérer les requêtes?
Pour gérer GetPostsQuery
, j'aurais besoin d'une liste de tous les messages. Cela signifie que mon modèle de lecture devrait avoir une liste de tous les messages. Je vais maintenir ce modèle en écoutant PostCreatedEvent
.
Pour gérer les deux GetFollowersQuery
et GetFollowedUsersQuery
, j'aurais besoin d'une liste de toutes les connexions entre les utilisateurs. Pour maintenir ce modèle, je vais écouter UserFollowedEvent
. Voici une question n ° 2 : est-ce pratiquement OK si j'utilise ici la liste des connexions du modèle d'écriture? Ou devrais-je mieux créer un modèle de lecture distinct, car à l'avenir, j'aurai peut-être besoin de plus de détails que le modèle d'écriture?
Enfin, pour gérer, GetFriendFeedRecordsQuery
je devrais:
- Ecouter
UserFollowedEvent
- Ecouter
PostCreatedEvent
- Savoir quels utilisateurs suivent quels autres utilisateurs
Si l'utilisateur A suit l'utilisateur B et que l'utilisateur B commence à suivre l'utilisateur C, les enregistrements suivants doivent apparaître:
- Pour l'utilisateur A: "Votre ami utilisateur B vient de commencer à suivre l'utilisateur C"
- Pour l'utilisateur B: "Vous venez de commencer à suivre l'utilisateur C"
- Pour l'utilisateur C: "L'utilisateur B vous suit maintenant"
Voici la question n ° 3 : quel modèle dois-je utiliser pour obtenir la liste des connexions? Dois-je utiliser le modèle d'écriture? Dois-je utiliser le modèle de lecture - GetFollowersQuery
/ GetFollowedUsersQuery
? Ou dois-je faire en sorte que le GetFriendFeedRecordsQuery
modèle gère lui-même la UserFollowedEvent
liste de toutes les connexions et la maintienne?