CQRS + Sourcing d'événements: (est-il exact que) les commandes sont généralement communiquées point à point, tandis que les événements de domaine sont communiqués via pub / sub?

12

J'essaie essentiellement de comprendre la notion de CQRS et les concepts connexes.

Bien que le CQRS n'intègre pas nécessairement la messagerie et la recherche d'événements, il semble être une bonne combinaison (comme on peut le voir avec de nombreux exemples / articles de blog combinant ces concepts)

Étant donné un cas d'utilisation pour un changement d'état pour quelque chose (disons pour mettre à jour une question sur SO), considéreriez-vous que le flux suivant est correct (comme dans les meilleures pratiques)?

Le système émet un UpdateQuestionCommand agrégé qui peut être séparé en quelques commandes plus petites: UpdateQuestion qui vise la racine d'agrégat de questions et UpdateUserAction (pour compter les points, etc.) ciblé sur la racine d'agrégats d'utilisateurs. Ceux-ci sont envoyés de manière asynchrone à l'aide de la messagerie point à point.

Les racines agrégées font leur travail et si tout se passe bien, déclenchez respectivement les événements QuestionUpdated et UserActionUpdated, qui contiennent un état qui est externalisé vers un magasin d'événements .. pour être persistant yadayada, juste pour être complet, pas vraiment le point ici.

Ces événements sont également placés dans une file d'attente pub / sub pour la diffusion. Tout abonné (parmi lesquels probablement un ou plusieurs projecteurs qui créent les vues de lecture) est libre de s'abonner à ces événements.

La question générale: est-il en effet de bonne pratique que les commandes soient communiquées point à point (ie: le récepteur est connu) alors que les événements sont diffusés (ie: le ou les récepteurs sont inconnus)?

En supposant ce qui précède, quel serait l'avantage / l'inconvénient de permettre la diffusion des commandes via pub / sub plutôt que point à point?

Par exemple: lors de la diffusion de commandes lors de l'utilisation de Saga, cela pourrait être un problème, car le rôle de médiation qu'une Saga doit jouer en cas de défaillance d'une des racines agrégées est entravé, car la saga ne sait pas à quelle racine agrégée participent pour commencer. .

D'un autre côté, je vois des avantages (flexibilité) lorsque la diffusion de commandes serait autorisée.

Geert-Jan
la source
Question bien écrite btw.
Dav

Réponses:

19

Disclaimler: Je ne fais que mes premiers pas dans le monde CQRS, mais je peux offrir ma compréhension actuelle de la question et nous verrons si d'autres le confirment. Tout ce que j'écris ci-dessous a un thème sous-jacent "tel que je le vois" et ne fait pas autorité.

Le cas à 80%

Pour répondre à votre question, les commandes sont en effet une affaire point à point. Lorsqu'une commande entre dans un contrôleur (application Web MVC), ce contrôleur demande ensuite à un répartiteur de commandes de trouver un et un seul gestionnaire de commandes approprié et délègue le travail à ce gestionnaire.

Pourquoi ne pas publier?

C'est une question de responsabilité . Si quelque chose envoie une commande, il faut s'attendre à ce qu'elle soit exécutée. Si vous publiez simplement et espérez que quelque chose le récupère et y donne suite, rien ne garantit que ce sera le cas. Par extrapolation, vous ne savez pas non plus si plusieurs gestionnaires ne décident pas d'agir sur une commande, ce qui peut entraîner l'application de la même modification plusieurs fois.

Les événements, en revanche, sont de nature informative, et il est raisonnable de s'attendre à ce que zéro, deux ou plusieurs composants soient intéressés par un événement particulier. Nous ne nous soucions pas vraiment de la portée de la modification demandée.

Exemple

Cela pourrait être comparé à la vraie vie. Si vous avez trois enfants, entrez dans une pièce et criez simplement "Nettoyez la salle de bain", vous n'avez aucune garantie que quelqu'un le fera, et vous préviendrez si cela ne se fait pas deux fois (si vous avez des enfants obéissants, c'est ;-) Vous devriez s'en tirer mieux si vous affectez un enfant spécifique à faire ce que vous voulez faire.

Cependant, lorsque cet enfant a terminé son travail, il est commode de crier «la salle de bain a été nettoyée», de sorte que tous ceux qui veulent se brosser les dents savent qu'ils peuvent désormais le faire.

Dav
la source
a beaucoup de sens. Excellente analogie :)
Geert-Jan
Tu m'as perdu à .. When a command enters a controller (MVC webapp)-? Utilisez-vous RESTful? ou certains points de terminaison API hybrides? Pourriez-vous ajouter un exemple s'il vous plaît
Piotr Kula
@ppumkin, nous avons utilisé un point de terminaison WebAPI qui serait appelé par notre application Web chaque fois qu'elle avait besoin d'exécuter une commande. Par exemple. si l'utilisateur voulait ajouter un commentaire de publication, l'application Web enverrait une demande POST à example.com/api/Post/AddPostComment.
Dav
1

Je suis d'accord qu'un système initiateur ne s'attendrait généralement pas à ce qu'une commande soit exécutée par plusieurs systèmes cibles:

  • Les commandes ne sont généralement jamais «envoyées et priées» - le système initiateur d'une commande souhaite généralement un retour asynchrone au fur et à mesure de l'avancement et du résultat de la commande (par exemple, des événements d'état comme acknowledgementet réussis completionou failurepeuvent être publiés par le système ciblé pour informer l'initiateur). système).
  • Si plusieurs systèmes cibles étaient impliqués, le système initiateur recevrait plusieurs résultats (éventuellement contradictoires) pour la commande (par exemple, la cible 1 a réussi, mais la cible 2 a échoué). Cela nécessiterait une complexité supplémentaire pour déterminer l'état réel de la commande d'origine (par exemple, la transaction de commande ne serait-elle considérée comme réussie que si toutes les cibles réussissaient? La commande devrait-elle être annulée ou compensée en cibles réussies si l'une des cibles échouait? etc.). Cela introduirait un couplage et une complexité indésirables entre les systèmes initiateur et cible.

Il y a cependant encore du mérite à pouvoir souscrire des consommateurs supplémentaires (non transactionnels et généralement assez «promiscuité») qui «écoutent» les commandes émises entre les systèmes

  • Objectifs d'audit
  • Instrumentation et mesures opérationnelles (p. Ex. Charge, analyse commerciale, etc.)
  • Traitement des événements complexes ou traitement des événements en continu «écoutes» - ces systèmes peuvent détecter des erreurs ou des irrégularités dans les commandes (par exemple, la fréquence des commandes ou la corrélation entre différentes combinaisons de commandes et d'événements) et peuvent déclencher des alertes ou des actions correctives (souvent dans le d'autres types de commandes).
StuartLC
la source