Où ces requêtes s'intègrent-elles dans une bonne architecture ?

Aug 17 2020

Je réfléchis à un problème depuis un moment et je ne pense pas vraiment avoir compris comment aborder cela.

J'essaie de travailler avec DDD autant que possible, mais certains scénarios de présentation ne correspondent pas vraiment à cela en termes de performances.

Disons que nous avons un système pour les voitures de location, et là nous avons une vue qui devrait afficher toutes les voitures et le nombre de fois qu'elles ont été louées.

Dans un monde DDD, je suppose que j'aurais un référentiel pour mes voitures de location et un référentiel pour les "locations". Je suppose également qu'il pourrait y avoir un service qui gère l'interaction entre eux, la création d'accords, etc.

Mais quand il s'agit de présenter la vue avec des voitures de location et le nombre de locations, je ne sais pas trop où mettre ça. Étant donné que les exigences sont très spécifiques à cette vue, je suppose que la version la plus performante serait d'avoir une sorte d'objet "RentalCarsOverview" renvoyé par le référentiel. Mais cela "polluerait" les référentiels avec des requêtes très spécifiques à la vue.

Donc je me demande si quelqu'un a rencontré les mêmes problèmes et comment cela a été résolu? J'ai regardé CQRS pour séparer cela, en utilisant des requêtes pour ce type de vues et mon modèle de domaine pour les mises à jour. Est-ce une bonne approche ?

En outre, serait-il "ok" d'avoir simplement du SQL brut dans ces gestionnaires de requêtes ou les résumerait-on également avec un référentiel sous-jacent ?

Réponses

3 VoiceOfUnreason Aug 17 2020 at 19:44

De nos jours, la réponse habituelle est que vos chemins de code de requête contourneront le "modèle de domaine" et fonctionneront directement avec des copies de vos informations.

L'argument ici étant qu'une requête ne modifie pas les informations stockées dans le modèle de domaine, et donc nous ne bénéficions pas d'un tas de cérémonies qui placent ces informations dans des objets de domaine juste pour les retirer à nouveau.

Par exemple, si toutes vos informations sont stockées dans la même base de données relationnelle, vous êtes très susceptible d'interroger la base de données et de transformer les informations dans les ensembles de résultats dans la forme correcte, puis de les renvoyer au client.

Lorsque les informations dont vous avez besoin pour répondre à la requête sont réparties sur plusieurs systèmes, il y a plus de travail sur mesure pour générer le rapport. Lorsque le temps requis pour générer le rapport dépasse vos objectifs de latence, vous commencez à rechercher des conceptions dans lesquelles vous pouvez mettre en cache les parties coûteuses du rapport. Dans certains cas, cela signifie que l'intégralité du rapport est mise en cache.

2 RikD Aug 17 2020 at 17:07

CQRS et DDD sont parfaitement compatibles.

Les commandes passent par vos référentiels et vos modèles DDD. Les requêtes peuvent être du SQL brut ou un ORM léger, avec des modèles distincts spécifiquement pour chaque vue.

L'architecture de votre système de cette manière vous donne un moyen solide de gérer des règles métier complexes, avec les meilleures performances possibles pour les lectures.

2 CarmineIngaldi Aug 17 2020 at 19:38

Commençons par souligner que le fait d'avoir des requêtes intelligentes est une odeur de code DDD. La plupart du temps, tout ce que vous avez à faire est de

  • récupérer un agrégat par son ID (en parlant RESTful : GET /users/xyz)
  • récupérer tous les agrégats avec une racine donnée ( /users?page=0&limit=10)
  • récupérer un sous-ensemble d'agrégats avec un filtre simple (en parlant RESTful : GET /users?username_startsWith=johnd&page=0&limit=10 )

Les applications complexes doivent faire des choses complexes, claires, de sorte que vous devrez peut-être créer des projections à partir de votre agrégat. Évidemment, votre agrégat ne devrait avoir aucune idée de ce qu'est une projection, mais vous pouvez y trouver toutes les données nécessaires pour les construire, car vous concevez votre domaine non seulement pour être constamment muté, mais aussi pour être observé.

Il s'agit d'une approche raisonnable même sans introduire les concepts CQRS. La plupart de nos applications sont des applications CRUD + quelques règles de validation de domaine. Si vous rencontrez des mots comme " aperçu " " rapport " " statistiques " " résumé " " historique " quelques fois dans vos exigences/histoires/code, vous savez que vous pouvez les gérer en chargeant simplement des agrégats en mémoire via des requêtes spécifiques, en appelant une agrégation méthode (comptages, sommes, moyennes) e faire le mappage par rapport à une projection DTO

Lorsque les cas susmentionnés sont importants dans votre projet, lorsque le chargement d'agrégats pour récupérer uniquement certaines informations agrégées est un énorme fardeau pour votre système, alors oui, vous rencontrez l'un des deux cas d'utilisation dans lesquels CQRS est utile

Vous fournirez à votre agrégat (à savoir, l'agrégat côté écriture) la possibilité de déclencher des événements de domaine chaque fois qu'une mutation d'état remarquable a été commise

Vous allez créer un module différent dans lequel vous disposerez de référentiels et de gestionnaires d'événements spécifiques à la projection (dans votre cas, quelque chose comme onEvent(CarRentedOutEvt evt) ) pour mettre à jour et enregistrer vos projections.

Vous aurez (encore une fois de manière reposante) une API d'administration GET /rents/overview qui se comportera très bêtement. Toute la complexité est déchargée au moment de la mise à jour

Merrion Sep 08 2020 at 17:48

Vous êtes autorisé à avoir plus d'un modèle de lecture et, dans ce cas, un modèle de lecture "Reporting" dénormalisé qui s'abonne uniquement aux événements nécessaires pour se mettre à jour lorsqu'il reçoit un événement "Rented Out".