【问题标题】:The right way to implement associations in DDD?在 DDD 中实现关联的正确方法?
【发布时间】:2013-05-30 13:26:37
【问题描述】:

我们第一次尝试在我们的项目中采用领域驱动设计。我遇到的问题是实体之间的关联。你是如何做到正确的?

说,我有实体EmployeeContract,一个简单的一对多关联。我该如何建模?

选项 1: 聚合。

问题:这里的问题是,如果我理解正确的话,在创建聚合对象时必须加载聚合中的所有实体。我不能在需要实体时延迟加载实体,因为它需要从实体引用存储库,这显然很糟糕。但是每次都从数据库中获取员工的所有合同将是一个很大的性能问题。

选项 2: 通过使用存储库(例如 ContractRepository.GetContractsForEmployee())获取员工的合同并将 EmployeeId 属性添加到 Contract 类。

问题:很难将任何业务逻辑放入实体中。我想要一个方法,比如Employee.Dismiss(),但它还需要更新员工的合同。这意味着我需要将此逻辑放入服务中。问题是,我想不出太多的逻辑只在 Employee 上运行,因此模型会变得有些贫乏,大部分逻辑都在服务内部。

您如何处理 DDD 中的这些问题?

【问题讨论】:

  • 一旦您考虑“简单的一对多关联”,您就不是在做 DDD,而是在做数据库模式设计。实体之间的关联应该是行为。员工不能解雇自己,必须解雇(由有权这样做的另一个实体)。选项 2 是正确的。您不必为实体提出很多行为。如果一个领域对象在实际业务中非常简单,那么就以这种方式建模。重要的是域如何定义概念(语义),而不是对象有多少方法。
  • MikeSW 是正确的。 DDD 的一个非常重要的方面是无处不在的语言。你谈论领域的方式必须是领域专家谈论领域的方式,否则代码会变得笨拙和不直观。这听起来微不足道,但如果你做对了,那么实体就会开始建议行为。例如员工.Resign(); Contract.Terminate();

标签: oop architecture domain-driven-design


【解决方案1】:

你需要找到你真正的不变量。

在这里你可以有一个不变量,例如:你不能解除已经被解除的Employee

如果这是唯一真正的不变量,那么您可以创建一个 Employee 聚合,它只有关联合约的 ID。

Contract 将是另一个聚合(如果需要)。

如果 dismiss() 方法成功,您可以加载所需的合约并进行必要的更改。

【讨论】:

    【解决方案2】:

    这只是我的看法...不知道您的域。

    首先,here 是一个很好的阅读资源(关于聚合和根的部分)。

    在 DDD 术语中,EmployeeContract 都是实体(因为它们都有一个身份)。

    “聚合围绕一个或多个实体绘制边界。并且:每个聚合都有一个根实体,它是聚合的唯一成员,允许聚合之外的任何对象保存引用。”

    问题是:EmployeeContract 是否形成聚合,Employee 是根实体?显然不是,因为其他域实体也可以引用 contract,并且合同 ID 是全球唯一的,不仅在 Customer 内。

    因此,考虑到这些规则,EmployeeContract 都是聚合根

    然后:“只有聚合根可以直接通过查询获得;所以这意味着我们应该为每个聚合根有一个存储库。”

    所以在这种情况下,我们有一个EmployeeRepository 和一个ContractRepository

    考虑到所有这些,我不会在域模型中添加员工和合同之间的关系;但分开对待。毕竟,如果你需要一个Employee,你不一定也需要他的contracts,它们是不同的方面。

    选项 2 是我会选择的:使用 ContractRepository 获取您感兴趣的合同。如果需要,您可以添加一个负责聚合员工和合同的域服务。

    如果您还定义了 Company 实体,则解雇员工可能是该实体的工作。

    【讨论】:

      【解决方案3】:

      我们最近也进入了 DDD 方法。如果我这样做,我会得到以下内容(为简洁起见,属性被简化):

      public class Employee() {
      
          String name;
          Set<ContractNumber> contracts;
      
          public void addContract(ContractNumber contractNumber) {
               this.contracts.add(contractNumber);
          }
      
      }
      
      public class Contract() {
          ContractNumber contractNumber;
          Date effectiveDate;
      }
      
      public class ContractNumber() {
          String contractNumber;
      }
      

      ContractNumber 是一个从 Employee 内部引用的值对象。在此示例中,Employee 位于处理Employees 及其各自合同的BoundedContext 中。在其他有界上下文中可能存在 Employee 的其他表示。

      正如在其他答案中所讨论的,Employee 和 Contract 都会有存储库。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 2021-12-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多