Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
543 views
in Technique[技术] by (71.8m points)

domain driven design - Repository Pattern: how to Lazy Load? or, Should I split this Aggregate?

I have a domain model that has the concept of an Editor and a Project.

An Editor owns a number of Projects, and a Project has not only an Editor owner, but also a number of Editor members. Therefore, an Editor also has a number of "joined" Projects.

I am taking a DDD approach to modelling this and using the Repository pattern for persistence. However, I don't grok the pattern well enough yet to determine how I should do this.

I'm working on the assumption that Editor and Project are potentially in the same aggregate, with the root being Editor. I can therefore get an Editor and then enumerate its Projects, and could from there enumerate the Projects' member Editors.

However, if I am only allowed to retrieve Editors from my repository, doesn't this mean I have to load all the Projects from the repository when I get the Editor that owns them? And if I want to lazy load the member Editors, the Project needs a reference to the repository as well?

Alternatively, if I split the aggregate and have an Editor repository and a Project repository, how should I handle a transaction across the two, such as when a new Project is added to an Editor? For example:

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

Am I misinterpreting the intent of the Repository pattern?

question from:https://stackoverflow.com/questions/458146/repository-pattern-how-to-lazy-load-or-should-i-split-this-aggregate

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Am I misinterpreting the intent of the Repository pattern?

I'm going to say "yeah", but know that me and every person I've worked with has asked the same thing for the same reason... "You're not thinking 4th dimensionally, Marty".

Let's simplify it a little and stick with constructors instead of Create methods first:

Editor e = new Editor("Editor Name");
e = editorRepository.Add(e);

Project p = new Project("Project Name", e);
p = projectRepository.Add(p);

Underneath, your project repository is always storing a valid owner (p.EditorId) into the project data as it's created, and however you re-populate an editor's projects, it will be there. This is why it's a good practice to put all required properties into constructors. If you don't want to pass the whole object, just the e.Id will do.

And if I want to lazy load the member Editors, the Project needs a reference to the repository as well?

Now, as to how to re-populate an editor's projects on demand, you have a couple of choices depending on what you're going for. Straight Repository says you want:

IEnumerable<Project> list = projectRepository.GetAllProjects()
                                .Where(x => x.editorId == e.Id);

But where to put it? Not inside Project, or Editor, you're right, or they will have to get access to repositories and that's no good. The above snippet is loosely coupled, but isn't reusable on its own. You've just reached the limits of Repository Pattern.

Next up is an Adapter Layer for your application, with a shared source of repositories (StaticServiceWrapper) and either some sort of EditorAdapter object (or Aggregate or whatever you'd call them) or now you can mix in extension methods that can talk to any and all necessary repositories fluently. I haven't done it exactly this way in a production system, but to show you a concise example:

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);
    }
}

Basically, once your GetAll,GetById,Add,Update,Remove Object Repository is done, you've got to leave the associations alone and move on up the object/layer hierarchy to the fun parts like Adapters and Caches and Business Logic ("Oh, my!").


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...