Comment les commandes Ajouter / Créer * doivent être gérées dans l'architecture CQRS + Event Sourcing

11

Je veux implémenter ma première application en utilisant le modèle CQRS avec Event Sourcing. Je me demande comment la création de racines agrégées doit être gérée correctement. Supposons que quelqu'un envoie la commande CreateItem. Comment cela devrait-il être géré? Où l'événement ItemCreated doit-il être stocké? Comme premier événement d'un nouvel article? Ou dois-je avoir une sorte d'entité ItemList qui regroupe tous les éléments et sa liste d'événements se compose uniquement d'événements ItemCreated?

Udi Dahan suggère de ne pas créer de racines agrégées et d'utiliser toujours à la place une sorte de méthode d'extraction. Mais comment je peux récupérer quelque chose de nouveau et qui n'a certainement aucun ID attribué. Je comprends l'idée derrière et il est assez raisonnable de penser qu'un nouvel objet est un objet qui a son état composé de zéro événements répondu sur lui. Mais comment dois-je l'utiliser? Dois-je avoir une méthode distincte dans mon référentiel comme getNewItem()ou faire get(id)accepter ma méthode à la Optional<ItemId>place?

Edit: Après un certain temps de fouille, j'ai trouvé une mise en œuvre très intéressante des modèles susmentionnés en utilisant des acteurs. L'auteur au lieu de créer l'agrégat, le récupère à partir d'une sorte de référentiel avec l'UUID nouvellement créé. L'inconvénient de cette approche est qu'il permet un état d'incohérence temporaire. Je me demande également comment mettre en œuvre la deleteméthode avec une telle approche. Ajoutez simplement l'événement supprimé à la liste d'événements de l'agrégat?

Mequrel
la source
1
Je soupçonne le post-titre d'Udi est trompeur. À mon humble avis, il semble que son véritable objectif est que les RA nouvellement créés soient toujours accessibles depuis un autre endroit, d'une manière qui saisisse le contexte sur pourquoi / comment / qui a décidé que le nouveau RA devait être créé. Tout le reste concerne la manière dont une implémentation particulière (NHibernate?) Pourrait faciliter sa gestion.
Darien
2
Notez que l'article d'Udi Dahan auquel vous faites référence mentionne spécifiquement que ses conseils peuvent ne pas s'appliquer à la recherche d'événements: udidahan.com/2009/06/29/dont-create-aggregate-roots/…
EZ Hart

Réponses:

13

L'idée dans le post d'Udi, si je comprends bien, est qu'aucun type d'article n'apparaît de nulle part. Il y a (presque) toujours quelque chose, ou plus précisément, une opération de domaine, qui a provoqué la création de l'élément. Tout comme l'exemple d'Udi d'un utilisateur né d'un visiteur s'inscrivant sur le site. À ce point et dans ce contexte délimité, Visitor est la racine agrégée, qui est récupérée par son adresse IP. Ce visiteur crée ensuite le nouvel «élément», un utilisateur à ce stade, via une opération de domaine appelée Register . Il en va de même pour l'étape précédente, qui est un autre contexte borné: le référent est l'AR, qui est récupéré par l'URL et qui a une opération de domaine appelée BeenVisitorWithIp , où le visiteur est né.

Udi écrit également très bien sur la suppression: http://www.udidahan.com/2009/09/01/dont-delete-just-dont/ . L'idée principale est que vous ne supprimez jamais rien. Il y a toujours une opération de domaine derrière, que nous voulons capturer. Comme une commande annulée plutôt que supprimée. Lisez-le, c'est un très bon article.

Le point principal ici sur les deux comptes, en faisant DDD et en particulier Event Sourcing, est que vous ne devriez jamais faire d'opérations CRUD droites. Si vous vous trouvez dans une situation où vous avez vraiment besoin d'insérer, de mettre à jour ou de supprimer des données, et qu'il n'y a vraiment aucune opération de domaine derrière, alors peut-être que DDD et Event Sourcing ne conviennent pas à ce contexte délimité . Vous êtes libre de combiner ces deux comme vous le souhaitez tant qu'un contexte borné unique adhère à un principe. De cette façon, le contexte délimité de style CRUD peut créer une ligne dans la base de données, qui devient une entité et une racine d'agrégat dans un autre contexte délimité, où vous pouvez maintenant récupérer l'AR sans avoir à le créer.

Tuukka Haapaniemi
la source
2
"peut-être que DDD et Event Sourcing ne conviennent pas à ce contexte délimité." Vous obtenez le point de DDD à droite. Il ne devrait pas être implémenté dans tous les cas juste pour la gloire de satan, mais seulement quand il faut traiter avec un domaine complexe plein de règles incertaines. Personnellement, je l'ai fait pour les logiciels juridiques où les exigences ne sont pas dictées par la logique.
Yegor Chumakov
2
+1 pour cette seule phrase "pour ce contexte délimité". :)
Songo
2
+1 l'utilisation des verbes «Ajouter» et «Créer» suggère fortement que vous pensez toujours à votre domaine en termes d'interaction avec une bonne vieille base de données tabulaire. Sans connaître votre domaine / contexte délimité, je ne peux pas dire si cela est approprié ou non. Ignorez la persistance, concentrez-vous d'abord sur les COMMANDES et les ÉVÉNEMENTS (alias les INTENTIONS et les RÉSULTATS) qui sont uniques à votre domaine, puis demandez-vous comment persister l'état, un problème qui a été résolu des centaines de milliers de fois auparavant.
Matt
"L'utilisation des verbes" Ajouter "et" Créer "suggère fortement que vous pensez toujours à votre domaine en termes d'interaction avec une bonne vieille base de données tabulaire" Hmmm. Lorsque vous avez une conception d'interface utilisateur qui contient un gros bouton «Ajouter quelque chose», alors malheureusement, c'est l'intention; littéralement pour ajouter quelque chose de nouveau. Je suis généralement d'accord avec vous, mais nous ne parlons pas de niveau de base de données ici, parfois ajouter ou créer sont en fait les bons mots à utiliser.
designermonkey
1
@designermonkey Lorsque vous avez ces boutons dans l'interface utilisateur, avez-vous vraiment une opération de domaine derrière eux? Peut-être, mais 9 fois sur 10, il n'y a vraiment pas besoin d'une opération de domaine complexe dans ce contexte délimité. Et l'opération CRUD pure n'est que cela, une opération CRUD pure, et doit être gérée comme telle. Ce n'est que lorsque la complexité du modèle de domaine est nécessaire qu'il doit être utilisé. Ainsi, les différents contextes délimités avec des principes de conception différents.
Tuukka Haapaniemi