Je plonge dans Domain Driven Design et certains des concepts que je rencontre ont beaucoup de sens en surface, mais quand j'y pense plus, je dois me demander si c'est vraiment une bonne idée.
Le concept d'agrégats, par exemple, a du sens. Vous créez de petits domaines de propriété afin de ne pas avoir à gérer l'intégralité du modèle de domaine.
Cependant, quand j'y pense dans le contexte d'une application Web, nous frappons fréquemment la base de données pour retirer de petits sous-ensembles de données. Par exemple, une page ne peut lister que le nombre de commandes, avec des liens sur lesquels cliquer pour ouvrir la commande et voir ses identifiants de commande.
Si je comprends bien les agrégats, j'utilise généralement le modèle de référentiel pour renvoyer un ordre d'agrégat qui contiendrait les membres GetAll
, GetByID
, Delete
et Save
. Ok, ça sonne bien. Mais...
Si j'appelle GetAll pour répertorier toutes mes commandes, il me semble que ce modèle nécessiterait le renvoi de la liste complète des informations agrégées, des commandes complètes, des lignes de commande, etc ... Lorsque je n'ai besoin que d'un petit sous-ensemble de ces informations (juste des informations d'en-tête).
Suis-je en train de manquer quelque chose? Ou y a-t-il un certain niveau d'optimisation que vous utiliseriez ici? Je ne peux pas imaginer que quelqu'un préconiserait de renvoyer des agrégats entiers d'informations lorsque vous n'en avez pas besoin.
Certes, on pourrait créer des méthodes sur votre référentiel comme GetOrderHeaders
, mais cela semble aller à l'encontre du but d'utiliser un modèle comme référentiel en premier lieu.
Quelqu'un peut-il clarifier cela pour moi?
MODIFIER:
Après beaucoup plus de recherches, je pense que la déconnexion ici est qu'un modèle de référentiel pur est différent de ce que la plupart des gens considèrent comme un référentiel.
Fowler définit un référentiel comme un magasin de données qui utilise la sémantique de collecte et est généralement conservé en mémoire. Cela signifie créer un graphique d'objet entier.
Evans modifie le référentiel pour inclure les racines d'agrégat, et donc le référentiel est amputé pour ne prendre en charge que les objets d'un agrégat.
La plupart des gens semblent considérer les référentiels comme des objets d'accès aux données glorifiés, où vous créez simplement des méthodes pour obtenir les données que vous souhaitez. Cela ne semble pas être l'intention décrite dans Patterns of Enterprise Application Architecture de Fowler.
D'autres encore considèrent un référentiel comme une simple abstraction utilisée principalement pour faciliter les tests et les moqueries, ou pour dissocier la persistance du reste du système.
Je suppose que la réponse est que c'est un concept beaucoup plus complexe que je ne le pensais au départ.
N'utilisez pas votre modèle de domaine et vos agrégats pour les requêtes.
En fait, ce que vous posez est une question suffisamment courante pour qu'un ensemble de principes et de modèles soit établi pour éviter cela. Il s'appelle CQRS .
J'ai eu du mal, et je continue de me battre, avec la meilleure façon d'utiliser le modèle de référentiel dans une conception pilotée par domaine. Après l'avoir utilisé pour la première fois, j'ai trouvé les pratiques suivantes:
Un référentiel doit être simple; il est uniquement responsable du stockage des objets de domaine et de leur récupération. Toutes les autres logiques doivent se trouver dans d'autres objets, comme les usines et les services de domaine.
Un référentiel se comporte comme une collection comme s'il s'agissait d'une collection en mémoire de racines agrégées.
Un référentiel n'est pas un DAO générique, chaque référentiel a son interface unique et étroite. Un référentiel a souvent des méthodes Finder spécifiques qui vous permettent de rechercher la collection en termes de domaine (par exemple: donnez-moi toutes les commandes ouvertes pour l'utilisateur X). Le référentiel lui-même peut être implémenté à l'aide d'un DAO générique.
Idéalement, les méthodes du Finder ne renverront que des racines agrégées. Si cela est inefficace, il peut également renvoyer des objets de valeur en lecture seule qui contiennent exactement ce dont vous avez besoin (bien que ce soit un avantage si ces objets de valeur peuvent également être exprimés en termes de domaine). En dernier recours, le référentiel peut également être utilisé pour renvoyer des sous-ensembles ou des collections de sous-ensembles d'une racine agrégée.
De tels choix dépendent des technologies utilisées, car vous devez trouver un moyen d'exprimer le plus efficacement possible votre modèle de domaine avec les technologies utilisées.
Je ne pense pas que votre méthode GetOrderHeaders vienne du tout à l'encontre de l'objectif du référentiel.
DDD est soucieux (entre autres) de s'assurer que vous obtenez ce dont vous avez besoin par le biais de la racine agrégée (vous n'auriez pas de OrderDetailsRepository, par exemple), mais cela ne vous limite pas dans la façon dont vous le mentionnez.
Si un OrderHeader est un concept de domaine, vous devez le définir comme tel et disposer des méthodes de référentiel appropriées pour les récupérer. Assurez-vous simplement que vous passez par la racine agrégée correcte lorsque vous le faites.
Mon utilisation de DDD peut ne pas être considérée comme DDD "pure" mais j'ai adapté les stratégies réelles suivantes en utilisant DDD par rapport à un magasin de données DB.
** Vous n'êtes pas obligé de rapporter un agrégat entier. Cependant, si vous en voulez plus, vous devez demander à la racine, pas à un autre service ou référentiel. Il s'agit d'un chargement paresseux et peut être effectué manuellement avec un chargement paresseux de mauvaise qualité (en injectant le référentiel/service approprié dans la racine) ou en utilisant et ORM qui prend en charge cela.
Dans votre exemple, je fournirais probablement un appel de référentiel qui n'a apporté que les en-têtes de commande si je voulais charger les détails sur un appel distinct. Notez qu'en ayant un "OrderHeader", nous introduisons en fait un concept supplémentaire dans le domaine.
Votre modèle de domaine contient votre logique métier dans sa forme la plus pure. Toutes les relations et opérations qui soutiennent les opérations commerciales. Ce qui vous manque dans votre carte conceptuelle, c'est l'idée de Application Service Layer la couche de service entoure le modèle de domaine et fournit une vue simplifiée du domaine d'activité (une projection si vous voulez) qui permet le modèle de domaine à modifier au besoin sans impact direct sur les applications utilisant la couche de service.
Aller plus loin. L'idée de l'agrégat est qu'il existe un objet, la racine de l'agrégat, responsable du maintien de la cohérence de l'agrégat. Dans votre exemple, la commande serait responsable de la manipulation de ses lignes de commande.
Pour votre exemple, la couche de service exposerait une opération comme GetOrdersForCustomer qui ne retournerait que ce qui est nécessaire pour afficher une liste récapitulative des commandes (comme vous les appelez OrderHeaders).
Enfin, le modèle de référentiel n'est pas JUSTE une collection, mais permet également des requêtes déclaratives. En C #, vous pouvez utiliser LINQ comme Query Object , ou la plupart des autres O/RM fournissent également une spécification Query Object.
Un référentiel sert d'intermédiaire entre les couches de mappage de domaine et de données, agissant comme une collection d'objets de domaine en mémoire. Les objets clients construisent les spécifications de requête de manière déclarative et les soumettent au référentiel pour satisfaction. (à partir de page du référentiel de Fowler )
Étant donné que vous pouvez créer des requêtes sur le référentiel, il est également judicieux de fournir des méthodes pratiques qui gèrent les requêtes courantes. C'est à dire. si vous souhaitez uniquement les en-têtes de votre commande, vous pouvez créer une requête qui renvoie uniquement l'en-tête et l'exposer à partir d'une méthode pratique dans vos référentiels.
J'espère que cela aide à clarifier les choses.
Je sais que c'est une vieille question, mais il semble que j'aie trouvé une réponse différente.
Quand je crée un référentiel, il encapsule généralement quelques requêtes en cache.
Fowler définit un référentiel comme un magasin de données qui utilise la sémantique de collecte et est généralement conservé en mémoire. Cela signifie créer un graphique d'objet entier.
Gardez ces référentiels dans la mémoire RAM de vos serveurs. Ils ne font pas que passer des objets à la base de données!
Si je suis dans une application Web avec une page listant les commandes, sur laquelle vous pouvez cliquer pour voir les détails, il y a de fortes chances que je veuille que ma page de liste de commandes contienne des détails sur les commandes (ID, Nom, Montant, Date) pour aider un utilisateur à décider lequel il souhaite consulter.
À ce stade, vous avez deux options.
Vous pouvez interroger la base de données et retirer exactement ce dont vous avez besoin pour créer la liste, puis interroger à nouveau pour extraire les détails individuels que vous devriez voir sur la page de détails.
Vous pouvez faire 1 requête qui récupère toutes les informations et les met en cache. Sur la page suivante, vous lisez à partir de la RAM des serveurs au lieu de la base de données. S'il l'utilisateur revient ou sélectionne la page suivante, vous ne faites toujours aucun voyage dans la base de données.
En réalité, la façon dont vous l'implémentez est juste cela, et les détails de l'implémentation. Si mon plus gros utilisateur a 10 commandes, je veux probablement aller avec l'option 2. Si je parle de 10 000 commandes, l'option 1 est nécessaire. Dans les deux cas ci-dessus et dans de nombreux autres cas, je souhaite que le référentiel masque ces détails d'implémentation.
À l'avenir, si je reçois un ticket pour dire à l'utilisateur combien il a dépensé pour les commandes ( données agrégées ) au cours du dernier mois sur la page de liste des commandes, est-ce que je préfère écrire la logique pour calculer cela en SQL et faire encore un aller-retour vers la base de données ou préférez-vous le calculer en utilisant les données qui sont déjà dans le serveur RAM?
D'après mon expérience, les agrégats de domaine offrent d'énormes avantages.