领域驱动设计:如何处理数据字段多的复杂模型?

Domain driven design: How to deal with complex models with a lot of data fields?

好吧,我正在尝试为我的应用程序应用域驱动设计原则,使用包含数据字段和业务逻辑的丰富域模型。我读过很多 DDD 书籍,但似乎他们的领域模型(称为实体)非常简单。当我有一个包含 10-15 个数据字段的域模型时,这就成了一个问题,例如下面的一个:

class Job extends DomainModel{

    protected int id;
    protected User employer;
    protected string position;
    protected string industry;
    protected string requirements;    
    protected string responsibilities;    
    protected string benefits;
    protected int vacancy;
    protected Money salary;
    protected DateTime datePosted;
    protected DateTime dateStarting;
    protected Interval duration;   
    protected String status;
    protected float rating;  

    //business logic below 
}

如你所见,这个领域模型包含了很多数据字段,而且都很重要,不能剥离。我知道一个好的富域模型不应该包含 setter 方法,而是将其数据传递给构造函数,并使用业务逻辑改变状态。但是,对于上面的域模型,我不能将所有内容都传递给构造函数,因为这会导致构造函数方法中的参数超过 15 个。一个方法不应该包含超过 6-7 个参数,你不觉得吗?

那么我该如何处理具有大量数据字段的领域模型呢?我应该尝试分解它吗?如果是这样,如何?或者,我应该只使用 Builder class 或反射在实例化时初始化其属性,这样我就不会用这么多参数污染构造函数?谁能给点建议?谢谢。

您错过了值对象的概念。值对象是小的、不可变的对象,在各自的域中具有意义。

我不知道您的域的具体情况,但查看您的 Job 实体,可能有一个值对象 JobDescription 如下所示:

class JobDescription {
    public JobDescription(string position, string requirements, string responsibilities) {
        Position = position;
        Requirements = requirements;
        Responsibilities = responsibilities;
    }

    public string Position {get;}
    public string Requirements {get;}
    public string Responsibilities {get;}
}

这是 C# 代码,但我认为无论您使用何种语言,思路都应该清晰。

基本思想是以在各自域中有意义的方式对值进行分组。这当然意味着值对象也可以包含其他值对象。

您还应该确保值对象是按值而不是按引用进行比较的,例如通过在 C# 中实现 IEquatable<T>

如果您使用这种方法重构您的代码,您的实体上将获得更少的字段,因此使用构造函数注入(强烈推荐)再次变得可行。


关于您的示例代码但与问题没有直接关系的进一步说明:

  • 领域模型是全部,实体是其中的一部分。所以你的基地 class 应该被称为 Entity 而不是 DomainModel.

  • 您应该创建 class private 的字段,并在需要维护封装的地方提供 protected 访问器。

您的工作域模型对象中发生了很多事情 - 它似乎混合了大量的关注点,并且(至少对我而言)暗示了许多有界上下文,其中一些很容易辨别为了举例说明。

  1. 薪酬(工资、福利)
  2. 组织职位(报告线)
  3. 人员规格(技能)
  4. 工作规范(职责)
  5. 等等

当您考虑与您的 'Job' 模型交互的事物时,是否有任何事物需要检查或改变工资 属性 和行业 属性,例如?

在不了解域的全部细微差别的情况下,您担任职位所获得的薪水与您从事的行业并没有真正联系,是吗?不是修辞点;这些是您需要询问领域专家的问题。

如果他们没有任何交互,那么你已经确定这两个东西存在于两个不同的边界上下文中。 Salary 端不需要与 Industry 端进行任何交互,反之亦然,即使它们交互了,它们是否需要同时在同一进程中保持状态?

想想一个人如何成为一名员工的生命周期;一个人申请一份工作。工作有规范,薪资范围。该人参加面试。雇用者向该人提供职位。该人接受。这个人现在是雇员,不再是候选人。新员工现在正在累积假期和福利,并有开始日期等。

DDD 告诉我们,单一、统一的世界观很少能正确地解决任何问题。请探索 BOUNDED CONTEXTS - 您的软件将因此变得更加柔韧和灵活。