DDD - Racine agrégée avec un grand nombre d'enfants


10

Je préfère cette question en disant que je suis relativement nouveau sur DDD, donc je peux faire des erreurs fondamentales ici!

Je travaille sur un projet qui implique les concepts de Comptes et Transactions (au sens financier). Un compte peut contenir plusieurs transactions.

Il me semble que le compte et la transaction sont tous deux des entités, et que le compte est une racine agrégée contenant des transactions, car une transaction ne peut pas exister sans le compte.

Cependant, lorsque je viens d'appliquer cela dans le code, j'ai immédiatement rencontré un problème. Dans de nombreuses situations, il n'est pas particulièrement utile pour moi d'avoir une liste de chaque transaction dans un compte à tout moment. Je souhaite pouvoir faire des choses comme calculer le solde du compte et appliquer des invariants tels qu'une limite de crédit, mais je veux également pouvoir travailler facilement avec un sous-ensemble de transactions (par exemple, afficher celles qui se situent dans une plage de dates).

Dans ce dernier cas, si j'utilisais un, TransactionRepositoryje pourrais accéder efficacement aux seuls objets dont j'ai besoin sans charger la liste entière (potentiellement très grande). Cependant, cela permettrait à d'autres choses que le compte de fonctionner avec les transactions, ce qui signifie que j'ai rompu le concept de compte en tant que racine agrégée.

Comment les gens gèrent-ils ce genre de situation? Acceptez-vous simplement les implications en termes de mémoire et de performances du chargement d'un nombre potentiellement énorme d'enfants pour une racine agrégée?

Réponses:


9

Je recommanderais d'être prudent avec la règle "ne peut exister sans" . Cela parle du concept de composition dans la conception UML / OO et a peut-être été l'une des approches prescrites pour concevoir des agrégats dans le livre bleu DDD original (je n'en suis pas sûr), mais a été largement révisé depuis. Il serait peut-être préférable de voir vos agrégats dans une perspective de limite de cohérence transactionnelle .

L'idée est de ne pas rendre vos agrégats trop grands, où vous auriez des problèmes de performances tels que celui que vous signalez, ni trop petits, car certains invariants s'étendraient inévitablement sur plusieurs agrégats, ce qui entraînerait des problèmes de verrouillage et de concurrence simultanée.

La bonne taille globale correspondrait idéalement aux contours de ce que vous modifiez dans une transaction commerciale donnée, ni plus ni moins. Dans votre exemple, s'il n'y a pas beaucoup d'invariants de domaine qui s'étendent sur plusieurs transactions financières, créer Transactionune racine agrégée à part entière pourrait bien être la meilleure solution.


Merci, je vais lire sur les limites de cohérence. Je pense que votre suggestion de faire de Transaction sa propre racine agrégée pourrait être une bonne idée; comme vous le dites, je n'ai pas beaucoup d'invariants couvrant plusieurs transactions.
krixon

7

tl; dr - enfreignez les règles si vous en avez besoin. DDD ne peut pas résoudre tous les problèmes; en fait, les idées d'objets qu'il donne sont de bons conseils et un bon début, mais de très mauvais choix pour certains problèmes commerciaux. Considérez cela comme un indice sur la façon de faire les choses.


Pour le problème de chargement de tous les enfants (transaction) avec le parent (compte) - Il semble que vous ayez rencontré le problème n + 1 (quelque chose à google) que de nombreux ORM ont résolu.

Vous pouvez le résoudre en chargeant paresseusement les enfants (transaction) - uniquement en cas de besoin.

Mais c'est comme si vous le saviez déjà en mentionnant que vous pouvez utiliser un TransactionRepository pour résoudre le problème.

Pour `` cacher '' ces données afin que seul le compte puisse les utiliser, vous ne devriez même pas les stocker là où personne d'autre ne voudrait les désérialiser, comme une table relationnelle publique. Vous pourriez l'avoir stocké avec le «document» de compte dans une base de données de documents. Quoi qu'il en soit, si quelqu'un essayait suffisamment, il pouvait toujours voir les données. Et «travailler» avec. Et quand vous ne regardez pas, ils le feront!

Vous pouvez donc configurer des autorisations, mais ensuite, vous devez exécuter «compte» en tant que processus distinct.

Ce que vous réalisez vraiment ici, c'est que DDD et l'utilisation pure du modèle d'objet vous ramèneront parfois dans un coin. En vérité, bien sûr, vous n'avez pas besoin d'utiliser la racine «composition» / agrégat pour bénéficier des principes de conception de DDD. C'est juste une chose que vous pouvez utiliser lorsque vous avez une situation qui correspond à ses contraintes.

Quelqu'un peut dire «ne pas optimiser tôt». Ici, dans ce cas, cependant, vous connaissez la réponse - il y aura suffisamment de transactions pour embourber une méthode qui les gardera toutes pour toujours avec le compte.

La vraie réponse est de commencer à tenir debout SOA. Sur mon lieu de travail, nous avons regardé les vidéos d'Udi Dahan «Calcul distribué» et acheté nServiceBus (juste notre choix). Créez un service pour les comptes - avec son propre processus, les files d'attente de messages, l'accès à une base de données de relations que seul il peut voir, et ... alto, vous pouvez coder en dur les instructions SQL dans le programme et même jeter quelques scripts de transaction Cobol (blague) bien sûr) mais ont sérieusement plus de séparation des préoccupations que le snob OO / Java le plus intelligent jamais imaginé.

Je recommanderais de bien le modéliser de toute façon; vous pouvez simplement profiter des avantages de root agrégé ici sans les problèmes en traitant le service comme un mini-texte délimité.

Cela a bien sûr un inconvénient. Vous ne pouvez pas simplement RPC (webservice, SOAP ou REST) ​​dans et hors des services et entre eux ou vous obtenez un anti-modèle SOA appelé «le nœud» en raison du couplage temporel. Vous devez utiliser l'inversion du modèle de communication, alias `` Pub-Sub '', comme pour les gestionnaires d'événements et les déclencheurs d'événements, mais (1) entre les processus (que vous pouvez placer sur des machines distinctes si elles sont surchargées sur un).

le vrai problème est que vous ne voulez pas qu'un service qui a besoin d'obtenir des données d'un autre service `` bloque '' ou attende - vous devez déclencher et oublier le message et laisser un gestionnaire ailleurs dans votre programme le récupérer pour terminer le traitement. Cela signifie que vous devez faire votre logique différemment. nServicebus automatise le modèle de «saga» pour aider à cela, mais à la fin, vous devez développer un style de codage différent. Vous pouvez toujours tout faire, il suffit de le faire différemment!

Le livre "SOA Patterns" par Arnon Rotem-Gal-Oz répond à de nombreuses questions à ce sujet. Y compris l'utilisation du `` modèle de service actif '' pour répliquer périodiquement les données des services externes sur les vôtres lorsque le besoin s'en fait sentir (de nombreux RPC auraient été nécessaires, ou le lien n'est pas fiable / n'est pas dans l'écosystème de publication / abonnement).

Juste pour aperçu, UIs ne doivent RPC dans les services. Les rapports sont générés à partir d'une base de données de rapports alimentée par les bases de données des services. Certaines personnes disent que les rapports ne sont pas nécessaires et que le problème devrait être résolu d'une autre manière. Soyez sceptique à l'égard de cette conversation.

En fin de compte, cependant, toutes les choses ne peuvent pas être correctement classées en un seul service. Le monde ne fonctionne pas sur le code des raviolis! Vous devrez donc enfreindre les règles. Même si vous n'auriez jamais à le faire, les nouveaux développeurs du projet le feront lorsque vous le quitterez. Mais ne vous inquiétez pas, si vous faites ce que vous pouvez, les 85% qui suivent les règles rendront un programme beaucoup plus maintenable.

Wow, c'était long.


Merci pour la réponse détaillée, je vais certainement faire un peu de lecture sur SOA.
krixon
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.