我的DDD逻辑属于哪里?

时间:2010-10-23 09:00:52

标签: oop design-patterns tdd domain-driven-design

我已经被Eric Evans的书所说服,并且正在将DDD集成到我的框架中。所有基本元素(服务,存储库,有界上下文等)都已实现,现在我正在寻找有关如何正确集成它的反馈。

我有一些业务逻辑,必须在创建或修改实体时执行。这个例子非常简单。大多数业务逻辑将变得更加复杂。

此业务逻辑可以分为以下几个步骤:

  1. 更新计算字段;
  2. 更新聚合根目录中的子记录。创建聚合根时,需要创建默认子记录。更新聚合根时,如果聚合根上的特定字段发生更改,则需要删除现有子记录并创建新记录;
  3. 将聚合根的开始和结束日期传播到聚合根目录中子记录的开始和结束日期。在某些情况下,这些必须保持同步;
  4. 将聚合根的字段传播到不同的聚合根。
  5. 我的第一次尝试是将所有这些放在聚合根上,但我觉得这不会起作用。我在集成这个逻辑时遇到以下问题:

    • 所有这些行动必须作为一个整体完成,不应作为单独的行动提供。结果是这将很难测试(TDD);
    • 我不清楚是否可以将这些操作转移到服务中。这样做的原因是它们在聚合根之外没有任何意义,但它会使TDD更容易;
    • 某些逻辑更改取决于是创建新实体还是修改现有实体。我应该将这两个分支放在更新逻辑中,还是应该创建两个完全不同的路径,这些路径共享不基于创建/修改的业务代码。

    对于上述问题的任何帮助都将受到高度赞赏,并提供其他反馈意见。

1 个答案:

答案 0 :(得分:6)

您描述的算法应该保留在聚合根中,否则您最终得到anemic domain model,除了将字段传播到另一个聚合根,我将在后面描述我认为您应该做的事情。

就TDD而言,聚合根上的“包”访问方法(例如“calculate()”)应该协调整个操作,服务或存储库对象通常会调用该操作。这就是测试应该与设置实例变量的不同组合一起运行。聚合根应该公开其实例变量,子集合,并且每个子项应该通过getter公开它的实例变量 - 这允许测试验证它们的状态。在所有情况下,如果你需要隐藏信息使这些getter包或私有访问,并使用你的单元测试框架将它们公开用于测试。

对于您的测试环境,请考虑mocking存储库对象(您正在使用dependency injection吗?)来返回硬编码值。考虑使用dbunit之类的东西来处理已知状态的数据库。

就逻辑变化而言,创建与修改有关,您指的是如何持久存在或者是否需要考虑实际算法?如果是前者,我会让存储库负责,如果后者我会做两个单独的方法(例如“calculateCreate()”&“calculateUpdate()”),其中calculate()将适当地委托。

此外,还有一个并发问题需要考虑,因为它听起来好像计算值依赖于可变字段。因此要么需要小心锁定,要么只能由客户端一次使用的聚合根。这也适用于跨聚合传播字段 - 我可能会将存储库用于此目的 - 但您需要仔细考虑这应该或不应该影响使用存储库对象的其他客户端。