代码之家  ›  专栏  ›  技术社区  ›  Giraffe

存储库模式:如何延迟加载?或者,我应该分割这个总数吗?

  •  39
  • Giraffe  · 技术社区  · 17 年前

    我有一个领域模型,它有一个编辑器和一个项目的概念。

    一个编辑器拥有许多项目,一个项目不仅拥有一个编辑器所有者,而且还拥有许多编辑器成员。因此,一个编辑器也有许多“联合”的项目。

    我采用DDD方法对此进行建模,并将存储库模式用于持久性。然而,我还没有很好地摸索出这个模式来决定我应该怎么做。

    我的工作假设编辑器和项目可能在同一个聚合中,根是编辑器。因此,我可以得到一个编辑器,然后枚举它的项目,并从中枚举项目的成员编辑器。

    但是,如果只允许我从我的存储库中检索编辑器,这是否意味着当我得到拥有它们的编辑器时,必须从存储库中加载所有项目?如果我想延迟加载成员编辑器,那么项目也需要一个对存储库的引用?

    或者,如果我拆分了聚合并有一个编辑器存储库和一个项目存储库,我应该如何处理这两个存储库之间的事务,例如当一个新项目添加到编辑器中时?例如:

    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
    

    我是否误解了存储库模式的意图?

    4 回复  |  直到 17 年前
        1
  •  30
  •   ifatree    17 年前

    我是否误解了存储库模式的意图?

    我要说“是的”,但要知道,我和我工作过的每个人都以同样的理由要求同样的事情…… “马蒂,你不是在第四维度思考。”

    让我们稍微简化一下,坚持使用构造函数,而不是先创建方法:

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

    在下面,您的项目存储库总是存储一个有效的所有者( p.EditorId )在创建项目数据时,它会出现在项目数据中,不管您如何重新填充编辑器的项目,它都会出现在那里。这就是为什么将所有必需的属性放入构造函数是一个好的实践。如果你不想通过整个物体,只要 e.Id 会的。

    如果我想延迟加载成员编辑器,那么项目也需要一个对存储库的引用?

    现在,关于如何根据需要重新填充一个编辑器的项目,您有两个选择,这取决于您要做什么。直库说你想要:

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

    但是放在哪里呢?不在项目或编辑器内部,你是对的,否则他们将不得不访问存储库,这是不好的。上面的代码片段是松散耦合的,但不能单独重用。您刚刚达到了存储库模式的限制。

    接下来是应用程序的适配器层,具有共享的存储库源( StaticServiceWrapper )或者某种类型的editoradapter对象(或者聚合对象,或者您可以调用它们的任何对象),或者现在您可以混合使用扩展方法,这些方法可以与任何和所有必要的存储库流畅地通信。我并没有在生产系统中这样做,但为了向您展示一个简明的示例:

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

    基本上,一旦您的getall、getbyid、add、update、remove对象存储库完成,您就必须将关联单独保存,并将对象/层层次结构向上移动到适配器、缓存和业务逻辑等有趣的部分。( “哦,我的天!” )

        2
  •  4
  •   Michael Hart    17 年前

    如何将责任划分为编辑所有者和编辑成员?

    在不了解您的领域的情况下,我可以想象他们有不同的职责——例如,编辑所有者可能相当富有(并且可能是聚合根),但项目可能只需要了解其成员的有限数量,因此编辑成员对象可能很轻。

    这些域对象也可能与用户相关,但这将出现在另一个上下文中。

    这有帮助吗,还是只是让事情变得更复杂?

        3
  •  3
  •   moffdub    17 年前

    这取决于应用程序的需要。如果为给定的编辑器加载所有项目是一个大问题,那么尝试像 Virtual Proxy .

    关于懒散地加载项目的成员编辑器,如果您使用虚拟代理,我不会看到用editorrepository插入代理的问题,因为我不认为代理是域的一部分。

    如果你把总数分开,你可以调查 Unit of Work 模式作为原子性的一种解决方案。不过,这个问题并不是DDD独有的,我相信还有其他解决方案可以解决事务行为。

        4
  •  0
  •   thinkbeforecoding    17 年前

    这里有两种不同的关系,一种是所有权关系,另一种是会员关系。

    所有权关系是简单的一对多(每个项目一个所有者)。成员关系是多对多的(多个编辑器按项目,多个项目按编辑器)。

    您可以在项目类上提供一个所有者属性,并在ProjectRepository上提供一个方法,以获取特定编辑器拥有的所有项目。

    对于多关系,在项目类上提供一个members属性,在ProjectRepository上提供一个方法,以获取包含指定编辑器的所有项目作为成员。

    似乎编辑器和项目都是实体,我可能会拆分聚合,但在您的上下文中,这些术语可能具有特定的含义,使其成为聚合的子实体。