Design patterns 聚合内部的实体是否可以在聚合外部访问或可见?

Design patterns 聚合内部的实体是否可以在聚合外部访问或可见?,design-patterns,architecture,domain-driven-design,software-design,Design Patterns,Architecture,Domain Driven Design,Software Design,我是DDD的新手,我的问题对你们中的许多人来说可能微不足道 以学生和课程为例 只有当学生的年龄高于注册该课程所需的最低年龄时,该学生才能注册该课程 在我看来,学生和课程可以被视为聚合,其中学生是根实体,课程是子实体,年龄是尊重的不变量 学生应该有一个方法Student.SubscribeTo(课程课程),该方法应该强制执行不变的Student.Age>=Course.MinAge,否则生成异常 这在DDD方法中是否正确?或者我应该只转到SubscribeTo CourseId吗?学生订阅(国际课

我是DDD的新手,我的问题对你们中的许多人来说可能微不足道

以学生和课程为例

只有当学生的年龄高于注册该课程所需的最低年龄时,该学生才能注册该课程

在我看来,学生和课程可以被视为聚合,其中学生是根实体,课程是子实体,年龄是尊重的不变量

学生应该有一个方法Student.SubscribeTo(课程课程),该方法应该强制执行不变的
Student.Age>=Course.MinAge
,否则生成异常

这在DDD方法中是否正确?或者我应该只转到SubscribeTo CourseId吗?学生订阅(国际课程ID)

从我的观点来看,如果没有办法打破不变量,那么应该允许在聚合外部访问课程。如果我在代码的其他地方更改了Course.MinAge,我不会违反我的业务要求,因为我只希望在订阅课程时尊重年龄,我不介意以后Course.MinAge是否会更改


如果业务需求状态不同,则情况不同:当Course.MinAge发生变化时,如果
Student.Age
我认为您拥有的合计不正确,则应将已注册课程的学生从课程中删除。课程实体可以独立存在,而不是学生实体的子实体。一门课程有它自己的生命周期:例如,如果一名学生离开学校,该课程将继续存在。课程id不依赖于学生id。学生可以持有课程id,但它们是不同的集合

无论如何,如果课程id是聚合,那么只将课程id传递给“student.subscribeTo”方法,答案是否定的,您不能将子实体的id传递给聚合的操作,因为子实体在聚合之外没有已知的全局标识。它们在聚合中具有本地id

更新:

由于课程和学生是两个集合,“学生的年龄必须高于注册课程所需的最低年龄”规则不是不变的。为什么?因为不变量是关于聚合状态的业务规则,所以它必须始终是事务一致的。聚合定义事务一致性边界

因此,该规则只是一个验证规则,当学生订阅课程时必须检查该规则(“student.subscribeTo”方法)。由于聚合不应使用存储库,因此可以将域服务传递给方法,学生聚合将双重分派到域服务,以便从课程id获取课程

请看沃恩·弗农(Vaughn Vernon)的红皮书IDDD(第361-363页)的聚合章节或同一作者的文章:


希望能有帮助。

我也在学习DDD,几天前我问了一个问题,你可以找到一个类似的问题

我学到的是,唯一真正的答案是:视情况而定。就其本身而言,没有正确或错误的方法,一切都必须为业务问题及其解决方案服务。尽管有一些指导原则和经验法则非常有用,例如:

  • 不要模仿现实。域模型是为解决特定问题而设计的抽象
  • 不要基于数据关系建模。每个关联都必须存在以强制执行规则或不变量,而不是因为这些对象在现实生活中是相关的。开始基于行为建模
  • 喜欢小骨料
  • 希望同时修改单个聚合(用例/事务),并使用最终一致性更新其他聚合
  • 仅通过标识在聚合之间建立关联
我认为你的方案中的问题是缺少很多。谁拥有这个协会?为什么?是否有其他跨学生和课程的用例?为什么你把
学生.订阅(课程)
而不是
课程.注册(学生)
?请记住,DDD的目标是处理复杂的域逻辑,因此当接近模型的写入端时,它会发光,而读取端可以以许多不同的方式完成,而无需将大量关联放入模型中

正如您所说,仅验证年龄可能不是一个固定的问题,这需要进行大量的汇总:

如果我在代码的其他地方更改
Course.MinAge
,我不会违反我的业务要求,因为我只希望在订阅课程时尊重年龄,我不介意以后
Course.MinAge
更改

那么就没有理由强制学生和课程在任何时候都保持一致(在这个特定的上下文/场景中),也没有必要让他们成为同一个集合的一部分。如果您需要执行的唯一规则是
Student.Age>=Course.MinAge
,那么您可以坚持一个简单的规则:

Student.SubscribeTo(Course course)
其中,
学生
课程
不属于同一集合。只要只修改一个聚合,加载两个不同的聚合并在同一事务中使用它们并没有什么不好的。(当然,在同一事务中修改两个聚合也没有什么不可取的,这只是一条经验法则,但在这种情况下,您可能不需要破坏它)

此处
Student.SubscribeTo
将强制执行有关年龄的规则。我不得不说,让
学生验证自己的年龄听起来很“奇怪”,但也许这正好适合你的模型(记住,不要模拟现实,而是模拟解决方案)。因此,
Student
将有一个新的状态保存课程的标识,
course
将保持不变

如果业务需求状态不同:当Course.MinAge更改时