web-dev-qa-db-fra.com

Commandes et requêtes CQRS - Appartiennent-elles au domaine?

Dans CQRS, les commandes et les requêtes appartiennent-elles au domaine?

Les événements appartiennent-ils également au domaine?

Si tel est le cas, les gestionnaires de commandes/requêtes ne sont-ils que des implémentations dans l'infrastructure?

En ce moment, je l'ai disposé comme ceci:

Application.Common
Application.Domain
  - Model
    - Aggregate
  - Commands
  - Queries
Application.Infrastructure
  - Command/Query Handlers
  - ...
Application.WebApi
  - Controllers that utilize Commands and Queries

Une autre question, d'où soulevez-vous les événements? Le gestionnaire de commandes ou l'agrégat de domaine?

34
Sam

Les commandes et Les événements peuvent être très différents. Il peut s'agir de soucis techniques, d'intégration, de domaine ...

Je suppose que si vous posez des questions sur le domaine, vous implémentez un modèle de domaine (peut-être même avec Domain Driven Design).

Si tel est le cas, je vais essayer de vous donner une réponse vraiment simplifiée, afin que vous puissiez avoir un point de départ:

  • Commande : est une intention commerciale, quelque chose que vous voulez qu'un système fasse. Conservez la définition des commandes dans le domaine. Techniquement, c'est juste un DTO pur. Le nom de la commande doit toujours être impératif " PlaceOrder", " ApplyDiscount" Une commande est gérée uniquement par un gestionnaire de commandes et peut être supprimée sinon valide (cependant vous devez rendre toute la validation possible avant d'envoyer la commande à votre domaine afin qu'elle ne puisse pas échouer)
  • Événement : c'est quelque chose qui s'est produit dans le passé. Pour l'entreprise, c'est le fait immuable qui ne peut pas être changé. Conservez la définition de l'événement de domaine dans le domaine. Techniquement, c'est aussi un objet DTO. Cependant, le nom de l'événement doit toujours être dans le passé " OrderPlaced", " DiscountApplied". Les événements sont généralement pub/sub. Un éditeur de nombreux gestionnaires.

Si tel est le cas, les gestionnaires de commandes/requêtes ne sont-ils que des implémentations dans l'infrastructure?

Les gestionnaires de commandes sont sémantiquement similaires à la couche de service d'application. Généralement, la couche de service d'application est responsable de l'orchestration du domaine. Il est souvent construit autour de cas d'utilisation métier comme par exemple "Passer une commande". Dans ces cas d'utilisation, invoquez la logique métier (qui doit toujours être encapsulée dans le domaine) via des racines agrégées, des requêtes, etc. C'est également un bon endroit pour gérer problèmes transversaux comme les transactions, la validation, la sécurité, etc. .

Cependant, la couche application n'est pas obligatoire. Cela dépend des exigences fonctionnelles et techniques et des choix d'architecture qui ont été faits. Votre laïcité semble correcte. Je ferais mieux de garder les gestionnaires de commande à la limite du système. S'il n'y a pas de couche d'application appropriée, un gestionnaire de commandes peut jouer un rôle d'orchestrateur de cas d'utilisation. Si vous le placez dans le domaine, vous ne pourrez pas gérer les problèmes transversaux très facilement. C'est un compromis. Vous devez connaître les avantages et les inconvénients de votre solution. Cela peut fonctionner dans un cas et pas dans un autre.

Quant aux gestionnaires d'événements. Je le gère généralement en

  • Couche d'application si l'événement déclenche la modification d'un autre agrégat dans le même contexte délimité ou si l'événement déclenche un service d'infrastructure.
  • Couche d'infrastructure si l'événement doit être divisé en plusieurs consommateurs ou intégrer un autre contexte délimité.

Quoi qu'il en soit, vous ne devez pas suivre aveuglément les règles. Il y a toujours des compromis et différentes approches peuvent être trouvées.

Une autre question, d'où soulevez-vous les événements? Le gestionnaire de commandes ou l'agrégat de domaine?

Je le fais à partir de la racine d'agrégat de domaine. Parce que le domaine est chargé de déclencher des événements. Comme il existe toujours une règle technique, vous ne devez pas publier d'événements si un problème persiste dans les modifications de l'agrégat et vice-versa, j'ai adopté l'approche utilisée dans Event Sourcing et c'est pragmatique. Ma racine agrégée possède une collection d'événements Unpublished. Dans l'implémentation de mon référentiel, j'inspectais la collection d'événements Unpublished et les transmettais au middleware responsable de la publication des événements. Il est facile de contrôler que si une exception persiste dans une racine agrégée, les événements ne sont pas publiés. Certains disent que ce n'est pas la responsabilité du référentiel, et je suis d'accord, mais peu importe. Quel est le choix. Vous avez un code maladroit pour la publication d'événements qui se glisse dans votre domaine avec toutes les préoccupations d'infrastructure (transaction, gestion des exceptions, etc.) ou qui est pragmatique et gère tout dans la couche Infrastructure? J'ai fait les deux et croyez-moi, je préfère être pragmatique.

Pour résumer, il n'y a pas une seule façon de faire les choses. Connaissez toujours les besoins de votre entreprise et vos exigences techniques (évolutivité, performances, etc.). Ensuite, faites vos choix en fonction de cela. J'ai décrit ce que j'ai généralement fait dans la plupart des cas et qui a fonctionné. C'est juste mon opinion.

45
Tomasz Jaskuλa

Dans certaines implémentations , les commandes et les gestionnaires se trouvent dans la couche Application. Inothers , ils appartiennent au domaine. J'ai souvent vu le premier dans les systèmes OO, et le second plus dans les implémentations fonctionnelles, ce qui est aussi ce que je fais moi-même, mais YMMV.

Si par événements, vous entendez les événements de domaine, eh bien ... oui, je recommande de les définir dans la couche Domaine et de les émettre à partir d'objets de domaine. Les événements de domaine sont une partie essentielle de votre langue omniprésente et seront même directement inventés par des experts du domaine si vous pratiquez Event Storming par exemple, il est donc logique de les y mettre.

Ce que je pense que vous devez garder à l'esprit, c'est qu'il n'y a pas de règle sur la plupart de ces détails techniques qui mérite d'être gravée dans le marbre. Il y a d'innombrables questions sur les projets de modèles DDD et la superposition et la "topographie" de code sur SO, mais franchement, je ne pense pas que ces problèmes soient décisifs pour faire une application robuste, performante et maintenable, d'autant plus qu'ils dépendent du contexte. Vous n'organiserez probablement pas le code d'un système de trading avec des millions de modifications globales par minute de la même manière que vous le feriez pour une plateforme de publication de blog utilisée par 50 personnes, même si les deux sont conçues avec une approche DDD. Parfois, vous devez essayer les choses par vous-même en fonction de votre contexte et apprendre en cours de route.

9
guillaume31

Les commandes et les événements sont des DTO. Vous pouvez avoir des gestionnaires de commandes et des requêtes dans n'importe quel calque/composant. Un événement est juste une notification que quelque chose a changé. Vous pouvez avoir tout type d'événements: domaine, application, etc.

Les événements peuvent être générés par le gestionnaire et agréger c'est à vous de décider. Cependant, quel que soit leur emplacement, le gestionnaire de commandes doit utiliser un bus de service pour publier les événements. Je préfère générer des événements de domaine à l'intérieur de la racine agrégée.

D'un point de vue stratégique DDD, il n'y a que des concepts commerciaux et des cas d'utilisation. Les événements de domaine, les commandes et les gestionnaires sont des détails techniques. Cependant, tous les cas d'utilisation de domaine sont généralement implémentés en tant que gestionnaire de commandes, par conséquent, les gestionnaires de commandes doivent faire partie du domaine ainsi que les gestionnaires de requêtes implémentant les requêtes utilisées par le domaine. Les requêtes utilisées par l'interface utilisateur peuvent faire partie de l'interface utilisateur, etc.

Le but du CQRS est d'avoir au moins 2 modèles et le Command devrait être le modèle de domaine lui-même. Cependant, vous pouvez avoir un modèle Query, spécialisé pour l'utilisation du domaine mais c'est toujours un modèle en lecture (simplifié). Considérez le modèle de commande comme étant utilisé uniquement pour les mises à jour, le modèle de lecture uniquement pour les requêtes. Mais, vous pouvez avoir plusieurs modèles de lecture (à utiliser par une couche ou un composant spécifique) ou simplement un modèle générique (utilisé pour tout).

2
MikeSW