DDD 工厂实体值对象

6

我在当前项目中越来越多地采用DDD/洋葱架构。 其中一个我仍然不清楚的问题是应该有多少封装性。 以具体示例更容易解释。

示例

namespace MyProject.Model
{
    public class ComplexEntity
    {
        private int _id;
        public int Id { get {return _id;} }
        public ValueObjectA ValueA {get; set;}
        public bool IsBool {get; set;}
        public ComplexEntity(ValueObjectA a, bool isBool)
        {
            // Do some validation first
            ValueA = a;
            ValueB = b;
            IsBool = isBool;
        }
    }

    public class ValueObjectA
    {
        public bool IsBoolA {get; private set;}
        public bool IsBoolB {get; private set;}
        public ValueObjectA(bool a, bool b)
        {
            IsBoolA = a;
            IsBoolB = b;
        }
    }

    public Interface IComplextEntityFactory
    {
        // Option 1
        ComplexEntity Create(
            ValueObjectA a,
            bool IsBool);

        // Option 2
        ComplexEntity Create(
            bool valueABool a,
            bool valueBBool b,
            bool isBool);
    }
}

问题

对于实体的工厂,你是:

  1. 期望调用方为你构建值对象并使用它来初始化 ComplexEntity 吗?
  2. 传递基本 CLR 类型到工厂,然后构造每个组成 Entity 的 ValueObject ?

我倾向于选项2,但似乎找不到支持这一观点的文献。

编辑1

说实话,我还是不清楚。聚合根呢?

当我的实体引用其他实体时,例如下面这样。

  1. Should I have an IComplexEntityFactory, ILessComplexEntityFactory? Or just an IComplexEntityAggregateFactory that creates the LessComplexEntity and instantiates ComplexEntity ?
  2. In the case of the AggregateFactory solution, what do I do if the LessComplexEntity attribtues that have been passed to the factory correspond to an existing LessComplexEntity? Do I retrieve and reuse it from a Repository? Or do I return an error to the caller?
  3. What would the method signature be for the AggregateFactory? Would it be (ValueObject a, ValueObject b), or (ValueObject value, LessCompelxEntity entity)

    public class ComplexEntity { private readonly int _id; public int Id { get { return _id;} }

    public ValueObject Value {get; set;}
    public LessComplexEntity Entity {get; set;}
    
    public ComplexEntity(int id, ValueObject value, LessComplexEntity entity)
    {
    }
    

    }

    public class LessComplexEntity { private readonly int _id; public int Id { get {return _id;} } public ValueObject Value {get; set;} public LessComplexEntity(int id, ValuObject value) { } }


我会选择第一种方式,否则如果您必须传递多个地址VO到采购订单中,那将非常繁琐。CreatePurchaseOrder(otherArgs,ShippingAddress,BillingAddress ...) - Roger Johansson
@RogerAlsing 但我不明白工厂的好处。这样做的话,它只是调用ComplexEntity的构造函数,而不是创建组成它的所有子组件。 - M Afifi
在我看来,这是关于语义的问题。例如,OrderFactory.CreateVIPCustomerOrder、OrderFactory.CreateAnonymousCustomerOrder等根据上下文不同的方式初始化聚合根... 它不应该只是另一层间接性。工厂方法可以比AR构造函数少很多参数,反之亦然。 - Roger Johansson
2个回答

5

我会选择选项1。

  • 明确告诉每个人需要一个ValueObjectA来构建ComplexEntity。更易读,当您在某个地方使用该方法时,不会让人感到困惑。

  • 如果ValueObjectA发生变化,您只需要修改代码中的一个地方(工厂的调用者),而不是更改Create()的签名+调整工厂内部的值对象创建。

  • 工厂的Create()方法参数较少,更简洁易读。

  • 在单元测试中,它为您提供了更多选项,以能够注入所需的ValueObjectA。如果将ValueObjectA的创建完全隐藏在工厂内部,则无法进行太多测试。

[编辑]

不清楚您对聚合根和工厂的真正问题是什么,但是不应混淆检索/重新激活现有对象的责任与创建对象的责任。

我认为工厂的工作是从较小的部分(无论是原始类型,值对象,实体等)组装一个新对象。

工厂应该提供所有这些部分,它不是从某个地方检索或重新激活它们的责任。您可以将其留给工厂的调用者。它会使工厂更具内聚性,并与较少的类(存储库等)耦合。


谢谢,这样解释有充分的理由,让我更加清楚了。但是我还不太明白聚合根(Aggregate Roots)是什么? - M Afifi

2
我更喜欢选项#1,因为它可以减少工厂方法所需的参数数量。但是我的建议是只有在聚合需要创建策略时才使用工厂。
在您的一条评论中,您说:
“...但是我不明白工厂的好处。这样做的唯一目的就是调用ComplexEntity的构造函数,而不是创建组成其的所有子组件。”
工厂的作用是通过查看传递给它的数据来决定如何实例化聚合。根据我的经验,这种情况通常发生在使用继承/多态性时。例如,想象一下你有一个抽象的Account类,有两个子类:SavingsAccount和CurrentAccount。工厂的工作是根据一些数据决定要实例化哪个。
另一个潜在的工厂优点是表达能力。如果一个聚合可以以多种方式实例化(即不同的参数),那么这可以通过工厂上的方法名比重载构造函数更好地表示。
正如我所说,我的建议是除非出于上述原因,否则不要为每个聚合创建工厂。否则,正如您指出的那样,它将只是一行调用构造函数,并传递相同的参数。

当存在一个聚合根并且人们传入与聚合中的相应实体匹配的参数时,该如何处理呢?是从存储库中检索它还是返回错误信息?谢谢。 - M Afifi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接