存储库,工厂和分层结构化数据

uri*_*ium 10 c# domain-driven-design ddd-repositories

我正在通过Eric Evans的领域驱动设计,他概述了存储库和工厂之间的相互作用.存储库本身将调用DB接口来获取结果集.然后将该结果集传递给工厂,该工厂将理解该结果集以重构该对象.

如果数据本质上是分层的,就像某种树结构那样.例如:

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Foo Parent { get; set; }
    public ICollection<Foo> { get; set; }

    // Other business like methods here

}
Run Code Online (Sandbox Code Playgroud)

使用DDD我会有我的接口和实现:

public interface IFooRepository
{
    Foo Get(int id);
}

public interface IFooFactory<TSource>
{
    Foo Create(TSource source);
}

public class SqlFooRepository: IFooRepository
{
    private readonly IFooDao dao;
    private readonly IFooFactory<SqlDataReader> factory;

    public SqlFooRepository(IFooDao dao, IFooFactory factory)
    {
        this.dao = dao;
        this.factory = factory;
    }

    public Foo Get(int id)
    {
        var resultSet = dao.Find(id);
        return factory.Create(resultSet);
    }
}

public class SqlFooFactory: IFooFactory<SqlDataReader>
{
    public Foo Get(SqlDataReader reader)
    {
        var foo = new Foo();
        foo.Id = (int) reader["id];
        foo.Name = (string) reader["name"];
            // Do I build the children accounts here
        return foo;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我试图在工厂建立孩子,那么我需要再次访问回购.如果我在Repo中这样做,我觉得我正在做的工作应该是工厂.不知道如何解决这个问题.

我有一个想法是,Foo不是聚合根,而是FooTree是聚合根.因此,我需要创建整个树,这意味着我可以将Foo对象的集合传递给FooTreeFactory.

任何帮助将非常感激.

Mar*_*ijn 3

假设您在使用 a 时需要Foo所有子级,您应该在存储库中获取它们并在工厂中重建层次结构。如果您不一定需要它们;或者您可能在某些极端情况下需要它们,您可以考虑延迟获取它们,但我认为这不是您在这里所追求的。

当构建这样的层次结构时,您需要确保只访问数据库一次。例如,如果您要Foo foo1在对数据库的一次调用中获取,然后foo1使用相同的存储库方法获取子项repo.Get(foo1.Id),那么您将为每个子项进行额外的数据库往返...如果您递归地进行此操作,则还会有更多次往返每个孩子也是如此。您不希望这样做,因为这会导致未知数量的额外数据库往返(选择 N+1 问题的变体)。

您想要的是一个可以在一次数据库往返中获取完整层次结构的存储库。如果您使用 ORM,那么 ORM 通常会内置一些东西来为您处理这个问题;例如NHibernate 就可以DistinctRootEntityResultTransformer做到这一点。

如果您想使用纯 sql 存储库来实现此目的,那么我将创建一个存储过程(假设您使用的是 Sql Server),该存储过程以递归方式Foo从数据库中获取层次结构中的所有行,并将它们以一个结果集返回到存储库。然后,存储库将此结果集传递给您的工厂以创建对象树。

因此,关键不是将 single 传递Foo给工厂,而是将 a 传递给读取结果集rowsreader的工厂,而不是单个行。Foo

更新

重新阅读你的问题后,我认为你和@enrico是正确的:

[...]FooTree是聚合根。因此,尝试获取任何Foo我需要创建整个树的内容,这意味着我可以将Foo对象集合传递给FooTreeFactory