web-dev-qa-db-fra.com

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

Je travaille sur un petit exemple d'application pour apprendre les concepts du CQRS et du sourcing d'événements. J'ai un agrégat Basket et un agrégat Product 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 événement ItemAdded sur l'agrégat Basket qui fait ce qu'il faut faire avec l'état du 'Basket'. 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 garder aussi DRY que possible
  3. 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 cela fuit des informations?
  4. Comment garder une relation entre les événements, comment savoir quel ItemAdded a produit quel ProductReserved événement? Dois-je transmettre un EventId? Cela semble étrange ...
  5. Dois-je implémenter le Basket en tant 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 travaille 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.

16
Ivan Pintar

Revoyez ce que Rinat Abdullin a écrit à propos de évolution du processus métier . 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'insisteriez pas pour réserver un inventaire avant de traiter une commande; cela ajoute des conflits inutiles à 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; qui 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 puis Décidez 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) est un no-op.

15
VoiceOfUnreason

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

3
Bishoy