DDD 工厂实体值对象

Posted

技术标签:

【中文标题】DDD 工厂实体值对象【英文标题】:DDD Factory Entity Value Object 【发布时间】:2012-07-08 20:24:26 【问题描述】:

我在当前的项目中越来越多地采用 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);
    

问题

对于一个实体的工厂,你呢,

    希望调用者为您构造值对象并使用它来初始化 ComplexEntity? 基本上已将 CLR 基本类型传递给工厂,并且您构造了构成实体的每个 ValueObject?

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

编辑 1

说实话,我还没有更清楚。聚合根呢?

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

    我应该有IComplexEntityFactoryILessComplexEntityFactory 吗?或者只是一个 IComplexEntityAggregateFactory 创建 LessComplexEntity 并实例化 ComplexEntity ? 在 AggregateFactory 解决方案的情况下,如果传递给工厂的 LessComplexEntity 属性对应于现有的 LessComplexEntity,我该怎么办?我是否从存储库中检索和重用它?还是我向调用者返回错误?

    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; 公共 int Id 获取 return _id; 公共值对象值 get;放; 公共 LessComplexEntity(int id,ValuObject 值)

【问题讨论】:

我会选择#1,如果您必须通过例如,那将非常乏味。否则,一个采购订单的多个地址 vo。 CreatePurchaseOrder( otherArgs , ShippingAddress, BillingAddress ...) @RogerAlsing 但是我不明白工厂的好处。它所要做的只是调用 ComplexEntity 的构造函数,而不是创建构成它的所有子组件。 它与语义 IMO、OrderFactory.CreateVIPCustomerOrder、OrderFactory.CreateAnonymousCustomerOrder 等有关。根据上下文不同地初始化聚合根......它不应该只是另一层间接。工厂方法的参数可能比 AR 构造函数少很多,反之亦然 @RogerAlsing 谢谢你说得通 【参考方案1】:

我会选择选项 1。

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

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

工厂的 Create() 方法的参数越少越简洁,可读性也更好。

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

[编辑]

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

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

应向工厂提供所有这些部件,它没有责任从某处取回或补充水分。你可以把它留给工厂的来电者。它将使 Factory 更多 cohesive 并耦合到更少的类(存储库等)

【讨论】:

谢谢,这更清楚了,有充分的理由。那么我不清楚 AggregateRoots 吗?【参考方案2】:

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

在你的一个 cmets 中你说:

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

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

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

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

【讨论】:

谢谢。当存在聚合根并且人们传入与作为聚合的一部分的相应实体匹配的参数时,情况会怎样。您是否从存储库中检索它?还是返回错误?

以上是关于DDD 工厂实体值对象的主要内容,如果未能解决你的问题,请参考以下文章

领域服务工厂资源库

DDD开发实践流程

DDD专栏5:深入DDD的核心:领域与限界上下文

领域驱动设计

在 NHibernate 中,我可以使用工厂来构建复杂的聚合对象(实体)吗?

使用 azure 数据工厂管道将 json 对象存储到 azure 表存储实体