DDD - Le référentiel racine d'un agrégat gère-t-il l'enregistrement des agrégats?

27

J'utilise une approche de type DDD pour un module entièrement nouveau d'une application existante; ce n'est pas 100% DDD en raison de l'architecture mais j'essaie d'utiliser certains concepts DDD. J'ai un contexte borné (je pense que c'est le terme approprié - j'apprends encore sur DDD) composé de deux entités: Conversationet Message. La conversation est la racine, car un message n'existe pas sans la conversation et tous les messages du système font partie d'une conversation.

J'ai une ConversationRepositoryclasse (bien que cela ressemble plus à une passerelle, j'utilise le terme "référentiel") qui trouve des conversations dans la base de données; lorsqu'il trouve une conversation, il crée également (via les usines) une liste de messages pour cette conversation (exposée en tant que propriété). Cela semble être la bonne façon de gérer les choses car il ne semble pas être nécessaire d'avoir une MessageRepositoryclasse complète car elle n'existe que lorsqu'une conversation est récupérée.

Cependant, quand il s'agit de sauvegarder un Message, est-ce la responsabilité du ConversationRepository, car c'est la racine agrégée du Message? Ce que je veux dire, est-ce que je devrais avoir une méthode sur ConversationRepository appelée, par exemple, AddMessagequi prend un Message comme paramètre et l'enregistre dans la base de données? Ou dois-je avoir un référentiel séparé pour trouver / enregistrer des messages? La chose logique semble être un référentiel par entité, mais j'ai également entendu "un référentiel par contexte".

Wayne Molina
la source

Réponses:

25

Le livre bleu vaut vraiment la peine d'être lu si vous voulez tirer le meilleur parti de l'approche DDD. Les modèles DDD ne sont pas triviaux et apprendre l'essence de chacun d'eux vous aidera à vous demander quand utiliser quel modèle, comment diviser votre application en couches, comment définir vos agrégats, etc.

Le groupe de 2 entités que vous mentionnez n'est pas un contexte délimité - c'est probablement un agrégat. Chaque agrégat a une racine d'agrégat, une entité qui sert de point d'entrée unique à l'agrégat pour tous les autres objets. Donc, aucune relation directe entre une entité et une autre entité dans un autre agrégat qui n'est pas la racine d'agrégat.

Les référentiels sont nécessaires pour mettre la main sur des entités qui ne sont pas facilement obtenues par la traversée d'autres objets. Les référentiels contiennent généralement des racines agrégées, mais il peut également y avoir des référentiels d'entités régulières.

Dans votre exemple, la conversation semble être la racine agrégée. Peut-être que les conversations sont le point de départ de votre application, ou peut-être que vous souhaitez les interroger avec des critères détaillés afin qu'elles ne soient pas accessibles de manière satisfaisante par une simple traversée d'autres objets. Dans un tel cas, vous pouvez créer un référentiel pour eux qui donnera au code client l'illusion d'un ensemble de conversations en mémoire à partir desquelles interroger, ajouter ou supprimer directement. D'un autre côté, les messages sont facilement obtenus en parcourant une conversation, et vous ne voudrez peut-être pas les obtenir selon des critères détaillés, juste tous les messages d'une conversation à la fois, donc ils n'auront peut-être pas besoin d'un référentiel.

ConversationRepository jouera un rôle dans la persistance des messages, mais pas un rôle aussi direct que vous le mentionnez. Donc, pas AddMessage () sur ConversationRepository (cette méthode appartient plutôt à Conversation elle-même) mais à la place, chaque fois que le référentiel persistera une conversation, c'est une bonne idée de conserver ses messages en même temps, soit de manière transparente si vous utilisez un framework ORM comme (N) Hibernate, en utilisant du SQL ad hoc si vous le souhaitez, etc.

guillaume31
la source
1
Si une racine agrégée, telle que Conversation, contient de nombreux types d'entités différents, tels que Message, Thingies et Wingies, lorsque vous enregistrez une conversation, par exemple ConversationRepo.save (conversation), comment savez-vous quelles entités en elle ont besoin être sauvé? Dans l'exemple d'affiches ci-dessus, seules les entités de message doivent être enregistrées. Parcourez-vous toutes les collections possibles dans la racine agrégée pour rechercher des entités sans ID?
chris-richards
3

Vous pouvez créer le ConversationService et injecter le IConversationRepository et IMessageRepository dans son constructeur. Utilisez des référentiels pour des opérations CRUD simples et des services pour tout le reste (mise en cache, sauvegarde de la logique, etc.)

šljaker
la source
1
ne sauvegarde pas la logique CRUD?
Timothy Groote