6

在我当前的项目中,我越来越多地采用 DDD / Onion 架构。我仍然不清楚的许多事情之一是应该有多少封装。用一个具体的例子更容易解释。

例子

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 基本类型传递给工厂,然后构造构成实体的每个 ValueObject?

我倾向于选项 2,但我似乎无法找到支持它的文献。

编辑 1

老实说,我仍然没有更清楚。聚合根呢?

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

  1. 我应该有一个IComplexEntityFactoryILessComplexEntityFactory?或者只是IComplexEntityAggregateFactory创建 LessComplexEntity 并实例化 ComplexEntity ?
  2. 在 AggregateFactory 解决方案的情况下,如果传递给工厂的 LessComplexEntity 属性对应于现有的 LessComplexEntity,我该怎么办?我是否从存储库中检索和重用它?还是我向调用者返回错误?
  3. AggregateFactory 的方法签名是什么?会不会(ValueObject a, ValueObject b),或者(ValueObject value, LessCompelxEntity entity)

    公共类 ComplexEntity { 私有只读 int _id; 公共 int Id { 获取 { 返回 _id;} }

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

    }

    公共类 LessComplexEntity { 私有只读 int _id; public int Id { get {return _id;} } public ValueObject Value {get; set;} public LessComplexEntity(int id,ValuObject 值){ } }

4

2 回答 2

5

我会选择选项1。

  • 向所有人明确表明您需要一个 ValueObjectA 来构建一个 ComplexEntity。当您在某处看到使用的方法时,更具可读性,更少头疼。

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

  • 工厂的 Create() 方法的参数越少越不冗长且可读性更强。

  • 在单元测试中,它为您提供了更多选项来注入所需的 ValueObjectA。如果您将 ValueObjectA 的创建完全隐藏在工厂中,则在测试方面您无能为力。

[编辑]

目前尚不清楚聚合根和工厂的真正问题是什么,但您不应将检索/再水化现有对象的职责与创建对象的职责混为一谈。

我认为工厂的工作是从较小的部分组装一个新对象作为一般规则(无论它们是原始类型、值对象、实体......)

应向工厂提供所有这些部件,从某处取回或再水化它们不是其职责。你可以把它留给工厂的来电者。它将使工厂更具凝聚力并与更少的类(存储库等)耦合

于 2012-07-09T14:26:04.427 回答
2

我更喜欢选项#1,因为它会减少工厂方法所需的参数数量。但我的建议是仅在相关聚合需要创建策略时才使用工厂。

在您的一条评论中,您说:

“..但是我不明白工厂的好处。它所做的只是调用 ComplexEntity 的构造函数,而不是创建构成它的所有子组件。”

工厂的工作是通过查看传递给它的数据来决定如何实例化聚合。根据我的经验,需要这样做的常见场景是使用了继承/多态性。例如,假设您有一个抽象 Account 类,它有两个子类:SavingsAccount 和 CurrentAccount。工厂的工作是在给定一些数据的情况下决定实例化哪一个。

工厂的另一个潜在好处是你的表现力。如果聚合可以以多种方式实例化(即不同的参数),那么使用工厂上的方法名称可以比重载的构造函数更好地表达。

正如我所说,我的建议是不要为每个聚合创建工厂,除非出于上述原因之一。否则,正如您所指出的,它只是调用构造函数并传递给它的相同参数的一行。

于 2012-07-09T14:01:45.230 回答