我正在通过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
}
使用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;
}
}
如果我尝试在工厂建立孩子,那么我需要再次访问回购。如果我在Repo中这样做,我觉得我正在做的工作应该是工厂。不知道如何解决这个问题。
我有一个想法是,Foo不是聚合根,而是FooTree是聚合根。所以试图得到任何Foo我需要创建整个树,这意味着我可以将Foo对象的集合传递给FooTreeFactory。
非常感谢任何帮助。
答案 0 :(得分:4)
存储库处理聚合根,而不是实体。
所以,我建议你使用FooTree
AR解决方案并从数据库中检索它。
工厂不应该依赖于存储库,因此您必须获取所有树数据并将它们传递给工厂。
或者,当AR客户端(或AR本身)请求它们时,您可以实现ORM的延迟加载和“延迟加载”子项。
答案 1 :(得分:3)
假设您在使用Foo
工作时需要所有孩子,您应该在存储库中获取它们并重建工厂中的层次结构。如果你不一定需要它们;或者你可能在某些角落的情况下需要它们,你可以考虑懒洋洋地取出它们,但我认为这不是你在这之后所做的。
构建这样的层次结构时,您需要确保只打一次数据库。例如,如果您在一次调用数据库中获取Foo foo1
,然后使用相同的存储库方法foo1
获取repo.Get(foo1.Id)
的子项,那么您将有一个额外的数据库往返对于每个孩子......如果你也为每个孩子递归,那么还有更多。您不希望这样,因为它会导致未知数量的额外数据库往返(select N+1 problem的变体)。
您想要的是一个存储库,它可以在一个数据库往返中获取您的完整层次结构。如果您使用的是ORM,那么ORM通常会内置一些东西来为您处理;例如NHibernate has a DistinctRootEntityResultTransformer
就是这样做的。
如果您想使用plain-sql存储库,那么我将创建一个存储过程(假设您使用的是Sql Server)从数据库中递归获取层次结构中的所有Foo
行并返回它们在一个结果集中的存储库。然后,存储库将此结果集传递给您的工厂以创建对象树。
所以关键是不要将单个Foo
传递给您的工厂,而是将reader
传递给工厂,该工厂读取结果集Foo
行,而不是单行。
更新
重新阅读你的问题之后,我认为你和@enrico都是正确的:
[...]
FooTree
是聚合根。所以我试图获得任何Foo
需要创建整个树,这意味着我可以传递一个集合Foo
个FooTreeFactory
对象的{{1}}
答案 2 :(得分:2)
Do not reference other AggregateRoot when designing Aggregate.
因此,在常见情况下,A Foo不会将其他Foos引用为树或父。如果确实需要,这可能是查询要求。例如,显示数据。 DDD不擅长查询,因此在没有如此多DDD限制和规则的情况下,使用贫血模型更容易实现。
<强>更新强>
如果此层次结构用于域派生,您可以考虑Specification pattern。
public class FooBarSpecification
{
public Foo Parent //injected by constructor
public ICollection<Foo> //injected by constructor
public boolean isSatisfiedBy(Foo foo) {
//use FooTree here to
}
}
客户可以使用FooRepository让Foos启动规范。
另一个解决方案是使用DomainService。