越来越多地关于 Aggregate Roots ,特别是来自Vaughn Vernon的I-DDD,我提出了一个关于并发性的问题。
许多用户同时访问系统。 (当前)核心域是关于教育中心的“注册”,因此有一个Student
实体代表企业的主要客户。 Student
是聚合根,当然还有很多信息。
假设Student
有一个PersonalAddress
和AcademicInformation
(过去的学校,成绩......),它们都被模拟为 Value Objects >聚合,并且 - 为了参数 - 在同一聚合中没有其他实体。 Student
聚合使用乐观并发与 Entity 中的版本属性,因此对其数据的任何更改都将增加该版本。
如果两个不同的用户同时尝试修改同一PersonalAddress
的{{1}}和AcademicInformation
,那么其中一个尝试将失败,即使地址和学术信息完全不相关;虽然两者都是 VO ,但它们属于相同的聚合。
我认为我可以拆分 Aggregate 以避免并发冲突,因为除了{属于'同一个{{}之外,没有与Student
和PersonalAddress
相关的真正不变量1}}。但那些 VO 没有自己的身份。我必须创建另一个 Entity 并将它们放在不同的 Aggregate Roots 中,两者都包含与同一学生相关的信息,因此可以同时修改它们。
所以问题是:
AcademicInformation
的同一实体(Student
)的无关信息的并发冲突,Student
)?PersonalAddress
聚合拆分为两个或多个不同的聚合根是一种“好方法”吗?我认为所有这些都取决于用户尝试同时修改信息的频率,并在此基础上决定分割聚合是否值得......但我不知道。
非常感谢你。
答案 0 :(得分:3)
1)您可以以其他方式使用乐观并发:基于旧值,而不是版本号。例如。命令改变学生应该看起来像新的ChaneStudentCmd {StudentId = ...; OldAddressStreet =“xxx”; NewAddressStreet =“yyy”},实现应该确保当前strret在更改之前是“xxx”。如果不是“xxx”,则应抛出并发异常。
2)我认为没有理由拆分聚合。
3)一般方法可以使用更具体的更新命令,而不是简单的“更新所有学生属性”。业务层应该包含有关该用户想要更新的信息。有了这些信息,它将能够优雅地处理更新,同时考虑到并发性和其他需求。
答案 1 :(得分:3)
在我看来,你可以保持原样,这是最简单的方法。但是,在一个Aggregate / Entity / Class中保持不相关的属性或方法会破坏“高内聚”规则。
所以我想知道你的情况是否是未被发现的有界背景的“气味”。 PersonalAddress
和AcademicInformation
是否属于同一个有界上下文?我不知道您的域名和用例,但您应该考虑这一点。
回答你的问题:
广告1.通过将不相关的信息分成不同的有界上下文或聚合来避免冲突。但是,相关信息仍然可能存在并发冲突。
Ad 2.正确地对有界上下文和聚合进行建模是一种“好方法”(虽然这并不容易;))。因此,并发冲突和不相关的信息是“气味”,让您知道您错过了模型中的某些内容。
广告3.再次,适当的有界上下文和聚合建模。
我并不是说在任何情况下都有办法避免通过分离BC和Aggreg所遇到的问题,但我确实认为这是可能的。我没有声称,在一个Aggregate中没有不相关的属性和方法总是如此,但它是“高内聚”规则引导我的地方。