Comment implémenter un gestionnaire de processus dans le sourcing d'événements

14

Je travaille sur un petit exemple d'application pour apprendre les concepts du CQRS et du sourcing d'événements. J'ai un Basketagrégat et un Productagrégat qui devraient fonctionner indépendamment.

Voici un pseudo code pour montrer l'implémentation

Basket { BasketId; OrderLines; Address; }

// basket events
BasketCreated { BasketId; }
ItemAdded { BasketId; ProductId; Quantity }
AddItemSucceeded { BasketId; ProductId; Quantity }
AddItemRevoked { BasketId; ProductId; Quantity }
ItemRemoved { BasketId; ProductId; Quantity }
CheckedOut { BasketId; Address }

Product { ProductId; Name; Price; }

// product events
ProductReserved { ProductId; Quantity }
ProductReservationFailed { ProductId; Quantity }
ProductReservationCancelled { ProductId; Quantity; }

Les commandes sont assez similaires aux événements, utilisant le nom impératif et non au passé.

En ce moment, cela fonctionne très bien indépendamment. J'émets une commande AddItem, et elle crée un ItemAddedévénement sur l' Basketensemble qui fait ce qu'elle doit faire avec l'état du «panier». De même, pour le produit, la commande et les événements fonctionnent très bien.

Je voudrais maintenant combiner cela en un processus qui se passerait comme ceci (en termes de commandes et d'événements qui se produisent):

Le gestionnaire de processus ferait ce qui suit:

on BasketCreated: CreateShoppingProcess
on ItemAdded: ReserveProduct
on ProductReserved: SucceedAddingItem // does nothing, but needs to be there so that the basket knows it can check out
on ProductReservationFailed: RevokeAddItem
on RemoveItem: CancelProductReservation
on Checkout: CreateOrder // create an order and so on...

Les questions auxquelles je n'ai pas pu trouver de réponses définitives sont:

  1. Dois-je persister le gestionnaire de processus? Il semble que ce soit le cas, mais je ne suis pas sûr
  2. Si je le fais, je dois enregistrer les événements pour le gestionnaire de processus. Cependant, les événements qu'il écoute sont liés aux agrégats. Dois-je ajouter l'ID de processus à ceux-ci? Ai-je des événements séparés uniquement pour le gestionnaire de processus? Comment faire cela et rester aussi SEC que possible
  3. Comment savoir à quel panier les ProductReservedévénements sont destinés? Est-ce OK d'avoir un BasketIdsur ceux-là aussi, ou est-ce une fuite d'informations?
  4. Comment puis-je garder une relation entre les événements, comment savoir qui a ItemAddedproduit quel ProductReservedévénement? Dois-je transmettre un EventId? Cela semble étrange ...
  5. Dois-je implémenter le en Baskettant que gestionnaire de processus au lieu d'un simple agrégat?

Après quelques recherches supplémentaires, je suis arrivé à ceci: Une saga est quelque chose qui garde ses propres événements et écoute les événements de l'extérieur. Fondamentalement, c'est un agrégat qui peut également réagir aux événements qui se produisent en dehors de son propre petit monde.

Un gestionnaire de processus fonctionne avec les événements de l'extérieur et envoie des commandes. Son historique peut être reconstruit à partir des événements qui se sont produits sur les agrégats qui partagent un identifiant commun comme un correlationId.

Ivan Pintar
la source
Il semble que vous essayez d'encoder dans votre système un processus formel qui paraphrase un cas d'utilisation informel existant constitué d'une série de commandes. En faisant cela, il semble que vous créez un certain nombre de commandes et d'événements redondants en plus des existants. Est-ce votre intention? Quel est le besoin commercial derrière la formalisation des choses en tant que processus dans le code? Qu'est-ce qui, dans votre domaine, vous oblige à identifier ce processus et à le raisonner comme un concept à part entière?
guillaume31
Il s'agit d'un projet totalement inventé dont le but est d'apprendre à faire fonctionner ensemble deux agrégats relativement indépendants. Il n'y a donc vraiment pas de «besoin métier» réel, et j'essaie d'éviter autant que possible la redondance dans ces commandes et événements. D'où la confusion avec le gestionnaire de processus., Car il semble qu'il ne faut pas répéter des choses que les agrégats gèrent déjà. Cependant, je dois en quelque sorte garder une connexion entre ces deux agrégats. Il semble que l'utilisation de la causalité et de la corrélation puisse aider, mais je dois l'essayer.
Ivan Pintar

Réponses:

14

Revoyez ce que Rinat Abdullin a écrit sur l' évolution des processus commerciaux . En particulier, notez sa recommandation de développer un processus métier dans un environnement en évolution rapide - un gestionnaire de processus est "juste" un remplacement automatisé pour un être humain qui regarde un écran.

Mon propre modèle mental de gestionnaire de processus est qu'il s'agit d'une projection provenant d'événements que vous pouvez interroger pour obtenir une liste des commandes en attente.

Dois-je persister le gestionnaire de processus? Il semble que ce soit le cas, mais je ne suis pas sûr

C'est un modèle de lecture. Vous pouvez reconstruire le gestionnaire de processus à partir de l'historique des événements chaque fois que vous en avez besoin; ou vous pouvez le traiter comme un instantané que vous mettez à jour.

Si je le fais, je dois enregistrer les événements pour le gestionnaire de processus.

Non - le gestionnaire de processus est un gestionnaire . Il ne fait rien d'utile en soi; au lieu de cela, il indique aux agrégats de faire le travail (c'est-à-dire d'apporter des modifications au livre des comptes).

Comment savoir à quel panier les événements ProductReserved sont destinés? Est-il correct d'avoir un BasketId sur ceux-ci aussi, ou est-ce que les informations qui fuient

Sûr. Remarque: dans la plupart des "vrais" domaines commerciaux, vous n'insisterez pas pour réserver un inventaire avant de traiter une commande; cela ajoute une contention inutile à l'entreprise. Il est plus probable que votre entreprise veuille accepter la commande, puis s'excuse dans les rares cas où la commande ne peut pas être exécutée dans le délai requis.

Comment garder une relation entre les événements, comment savoir quel ItemAdded a produit quel événement ProductReserved?

Les messages ont des métadonnées - en particulier, vous pouvez inclure un causationIdentifier (afin que vous puissiez identifier quelles commandes ont produit quels événements) et un correlationIdentifier , pour suivre généralement la conversation.

Par exemple, le gestionnaire de processus écrit son propre id en tant que correlationId dans la commande; les événements produits par une copie de l'ID de corrélation de la commande, et votre gestionnaire de processus s'abonne à tous les événements qui ont son propre correlationId.

Dois-je implémenter le panier en tant que gestionnaire de processus au lieu d'un simple agrégat?

Ma recommandation est non. Mais Udi Dahan a une opinion différente que vous devriez revoir; c'est que CQRS n'a de sens que si vos agrégats sont des sagas - Udi a utilisé la saga à l'endroit où le gestionnaire de processus est devenu l'orthographe dominante.

les gestionnaires de processus devraient-ils récupérer les agrégats?

Pas vraiment? Les gestionnaires de processus sont principalement concernés par l'orchestration et non par l'état du domaine. Une instance d'un processus aura un "état", sous la forme d'un historique de tous les événements qu'ils ont observés - la bonne chose à faire en réponse à l'événement Z dépend de la présence ou non d'événements X et Y Ainsi, vous devrez peut-être être en mesure de stocker et de charger une représentation de cet état (qui pourrait être quelque chose de plat, ou pourrait être l'historique des événements observés).

(Je dis «pas vraiment» parce que l' agrégat est défini de manière assez vague pour qu'il ne soit pas complètement faux de prétendre que la liste des événements observés est un «agrégat». Les différences sont plus sémantiques que l'implémentation - nous chargeons l' état du processus et décidons ensuite à quels messages envoyer aux parties du système responsables de l' état du domaine . Il y a un peu de main qui se passe ici.)

Ainsi, le PM n'a pas besoin d'utiliser un type de gestion d'état par rapport à un autre car il est uniquement responsable de faire des choses en direct et jamais pendant les relectures?

Pas tout à fait - la gestion de l'État n'est pas un do-er, c'est un traqueur de garde. Dans les circonstances où le gestionnaire de processus ne doit émettre aucun signal, vous lui donnez des connexions inertes avec le monde. En d'autres termes, dispatch(command)c'est un no-op.

VoiceOfUnreason
la source
1
Vous dites: Vous pouvez reconstruire le gestionnaire de processus à partir de l'historique des événements chaque fois que vous en avez besoin ... Mais pour le reconstruire, je dois enregistrer les événements pour cela. Ou devrais-je le reconstruire à partir des événements dans les agrégats? La partie avec laquelle je me bats est: avec les agrégats, les événements ont l'ID d'agrégat, et il est facile de reconstruire en trouvant tous les événements avec cet ID d'agrégat. Mais comment ferais-je cela pour le gestionnaire de processus? Dois-je le faire pour le gestionnaire de processus? Ou le gestionnaire de processus doit-il rechercher les agrégats lorsqu'il doit décider quelque chose en fonction d'un événement qui s'est produit?
Ivan Pintar
Ce qui me manquait, c'était la notion de causalité et de corrélation dans la recherche d'événements. Une fois que j'ai examiné cela, votre réponse à la quatrième question a finalement pris tout son sens.
Ivan Pintar
1
J'aimerais une réponse au premier commentaire de @IvanPintar; les gestionnaires de processus devraient-ils récupérer les agrégats? Devraient-ils construire le leur en fonction des événements qu'il traite? Dans ce cas, ses gestionnaires d'événements devraient être exempts d'effets secondaires, non?
Jeff
@Jeff Dans un exemple que j'ai fait, le gestionnaire de processus avait son propre stockage qui était mis à jour avec chaque événement traité, une sorte de modèle de lecture. C'était simple à faire et il était facile de suivre ce qu'il avait déjà traité. Dans un autre exemple, le gestionnaire de processus a créé et stocké ses propres événements, construits à partir des événements agrégés. Semblable à ci-dessus, mais l'État était d'origine événementielle. Selon la complexité de l'état que le gestionnaire de processus conserve, il peut être plus facile de faire l'un ou l'autre. J'ai trouvé la première approche plus simple.
Ivan Pintar
Intéressant, c'est donc plus ou moins au développeur tant que le gestionnaire de processus répond aux événements et envoie des commandes à la fin?
Jeff
2

Ce que vous recherchez a un modèle appelé "Saga", qui est essentiellement un gestionnaire de processus.

Les saga sont également parfaites pour les processus de longue durée, car elles peuvent maintenir l'état entre les commandes corrélées.

Voici un excellent article sur les Sagas

Bishoy
la source