Motif de dépôt: comment charger paresseusement? ou devrais-je diviser cet agrégat?
j'ai un modèle de domaine qu'est le concept d'un Éditeur et d'un Projet.
Un éditeur possède un certain nombre de projets, et un projet a non seulement un éditeur propriétaire, mais aussi un certain nombre de membres. Par conséquent, un éditeur a aussi un certain nombre de projets "joints".
j'adopte une approche de DDD pour modéliser ceci et utiliser le modèle de dépôt pour la persistance. Cependant, je ne grok le modèle assez bien pour déterminer comment je devrais faire cela.
je suis travailler sur l'hypothèse que L'éditeur et le projet sont potentiellement dans le même agrégat, la racine étant L'éditeur. Je peux donc obtenir un éditeur et ensuite énumérer ses projets, et pourrait à partir de là énumérer les éditeurs membres des projets.
cependant, si je suis seulement autorisé à récupérer des éditeurs à partir de mon dépôt, cela ne signifie-t-il pas que je dois charger tous les projets à partir du dépôt lorsque j'obtiens L'éditeur qui les possède? Et si je veux charger paresseusement les éditeurs membres, le projet besoin d'une référence au référentiel?
sinon, si je divise l'agrégat et que j'ai un dépôt Editeur et un dépôt projet, Comment dois-je gérer une transaction entre les deux, par exemple quand un nouveau projet est ajouté à un éditeur? Par exemple:
Editor e = new Editor("Editor Name");
editorRepository.Add(e);
Project p = e.CreateProject("Project Name");
projectRepository.Add(p); // These two lines
editorRepository.Save(e); // should be atomic
Est-ce que j'interprète mal l'intention du modèle de dépôt?
4 réponses
est-ce que j'interprète mal l'intention du motif du dépôt?
je vais dire "oui", mais sachez que moi et toutes les personnes avec qui j'ai travaillé ont demandé la même chose pour la même raison... "Vous n'êtes pas en pensant 4ème dimensions, Marty".
simplifions un peu et collons avec les constructeurs au lieu de créer les méthodes en premier:
Editor e = new Editor("Editor Name");
e = editorRepository.Add(e);
Project p = new Project("Project Name", e);
p = projectRepository.Add(p);
en dessous, votre dépôt de projet stocke toujours un propriétaire valide (p.EditorId
) dans les données du projet au moment de sa création, et quelle que soit la manière dont vous ré-peuplez les projets d'un éditeur, il sera là. C'est pourquoi il est une bonne pratique pour mettre toutes les propriétés requises dans les constructeurs. Si vous ne voulez pas passer tout l'objet, juste le e.Id
va faire.
et si je veux charger paresseusement les éditeurs membres, le projet a aussi besoin d'une référence au dépôt?
maintenant, quant à la façon de reconstituer les projets d'un éditeur sur demande, vous faites quelques choix selon ce que vous voulez. Droite Référentiel dit que vous voulez:
IEnumerable<Project> list = projectRepository.GetAllProjects()
.Where(x => x.editorId == e.Id);
Mais où le mettre? Pas dans Project, ou Editor, vous avez raison, ou ils auront accès à des dépôts et c'est pas bon. L'extrait ci-dessus est vaguement couplé, mais n'est pas réutilisable en soi. Vous venez d'atteindre les limites du motif du dépôt.
Next up est une couche adaptable pour votre application, avec une source partagée de dépôts (StaticServiceWrapper
) et soit une sorte D'objet EditorAdapter (ou Aggregate ou peu importe comment vous les appelez) ou Maintenant vous pouvez mélanger des méthodes d'extension qui peuvent parler à n'importe quel et tous les dépôts nécessaires couramment. Je ne l'ai pas fait exactement de cette façon dans un système de production, mais pour vous montrer un exemple concis:
public static class Aggregators
{
// one to one, easy
public static Editor GetOwner(this Project p)
{
return StaticServiceWrapper.editorRep.GetEditorById(p.editorId);
}
// one to many, medium
public static IEnumerable<Project> GetProjects(this Editor e)
{
return StaticServiceWrapper.projectRep.GetAllProjects()
.Where(x => x.editorId == e.Id);
}
// many to many, harder
public static IEnumerable<Editor> GetMembers(this Project p)
{
var list = StaticServiceWrapper.projectMemberMap.GetAllMemberMaps()
.Where(x => x.projectId == p.projectId);
foreach ( var item in list )
yield return StaticServiceWrapper.editorRep.GetEditorById(item.editorId);
}
}
fondamentalement, une fois que votre Getall,GetById,Add,Update,Remove Object Repository est fait, vous devez laisser les associations tranquilles et vous déplacer sur la hiérarchie objet/layer vers les parties amusantes comme Adaptateurs et Caches et logique D'entreprise ( " Oh, mon Dieu!").
Que Diriez-vous de partager les responsabilités en un propriétaire D'éditeur et un membre D'édition?
sans connaître votre domaine, j'imagine qu'ils auraient des responsabilités différentes - par exemple, L'éditeur pourrait être très riche (et pourrait être la racine agrégée), mais le projet pourrait n'avoir besoin de connaître qu'une quantité limitée de ses membres, donc L'objet EditorMember peut être assez léger.
ces objets de domaine peuvent aussi se rapporter à des utilisateurs, mais ce serait dans un autre cadre.
est-ce que cela aide les choses, ou juste les rend plus compliquées?
Cela dépend des besoins de votre application. Si c'est un gros problème pour charger tous les Projets, pour un Éditeur, puis essayer le chargement paresseux motif comme une Mandataire Virtuel.
en ce qui concerne le chargement paresseux des éditeurs membres d'un projet, si vous utilisez le Proxy virtuel, Je ne vois pas de problème à injecter le proxy avec L'EditorRepository puisque je ne considère pas le proxy comme faisant partie du domaine.
si vous divisez L'agrégat, vous pouvez Unité de Travail modèle comme une solution à l'atomicité. Ce problème, cependant, n'est pas unique à DDD et je suis sûr qu'il ya d'autres solutions pour le comportement transactionnel.
ici vous avez deux relations différentes, une pour la propriété et l'autre pour l'adhésion.
la relation de propriété est simple pour plusieurs (un propriétaire pour chaque projet). La relation de membre est beaucoup à beaucoup (beaucoup D'éditeurs par projet, beaucoup de projets par éditeur).
vous pouvez fournir une propriété propriétaire sur la classe de projet, et fournir une méthode sur le ProjectRepository pour obtenir tous les projets appartenant à un éditeur spécifique.
Pour les nombreuses relations, fournir des une propriété Members sur la classe Project, et une méthode sur ProjectRepository pour obtenir tous les projets contenant Editeur spécifié en tant que membre.
il semble aussi que les éditeurs et les projets sont des entités, Je diviserais probablement l'agrégat, mais peut-être que ces termes ont une signification spécifique dans votre contexte qui en fait des sousentités d'un agrégat.