Domain driven design 当列表中存在不变量时定义聚合根
我正在做一个家庭日托应用程序,我想我应该试试DDD/CQRS/ES,但我在设计好这些应用程序时遇到了一些问题。该领域可以简单地描述为:Domain driven design 当列表中存在不变量时定义聚合根,domain-driven-design,cqrs,event-sourcing,aggregateroot,Domain Driven Design,Cqrs,Event Sourcing,Aggregateroot,我正在做一个家庭日托应用程序,我想我应该试试DDD/CQRS/ES,但我在设计好这些应用程序时遇到了一些问题。该领域可以简单地描述为: 孩子入学了 一个孩子可以到达 孩子可以离开 目标是跟踪访问时间,生成发票,记录访问记录(如午餐内容、受伤等)。到目前为止,这些其他操作将是与系统最常见的交互,因为访问每天开始一次,但有趣的事情一直在发生 我正在努力解决的不变量是: 如果孩子已经到了,他们就不能来了 据我所知,我有以下几种选择 1。单个聚合根子 创建单个Childaggregate roo
- 孩子入学了
- 一个孩子可以到达
- 孩子可以离开
- 如果孩子已经到了,他们就不能来了
子
创建单个Child
aggregate root,其中包含事件childregistered
、ChildArrived
和ChildLeft
这似乎很简单,但由于我希望每个其他事件都与访问关联,这意味着访问将是子项
聚合的一个实体,每次我想添加注释或任何内容时,我都必须为该子项寻找所有访问的来源。看起来效率很低,也很不相关——孩子本身,以及每一次拜访,都与孩子午餐吃什么无关
2。聚合子项的根
和访问
Child
将只获取childregistered
,而Visit
将获取ChildArrived
和ChildLeft
。在这种情况下,我不知道如何维护不变量,除了让Visit
接受一项仅用于此目的的服务之外,我已经看到这是不鼓励的
有没有其他方法可以使用此设计强制执行不变量
3。这是一个假不变量
我想这是可能的,我应该防止多人同时登录同一个孩子,或者延迟意味着用户多次点击“登录”按钮。我认为这不是答案
4。我错过了一些明显的东西
这似乎是最有可能的-当然这不是一些特殊的雪花,这通常是如何处理的?我几乎找不到有多个AR的示例,更不用说有列表的示例了。听起来像是你的不变量中的“如果孩子已经在这里,他们就无法到达”中的“这里”可能是一个聚合的想法。可能是位置
或日间护理中心
。从这里开始,确保子不能到达两次似乎很简单,除非他们以前离开过
当然,那么这个总量将是相当长寿的。然后您可以考虑一个集合用于<代码>营业日<代码>或类似于限制孩子到达和离开的原始计数。
只是个主意。不一定是解决这个问题的方法。
您正在大量谈论访问
以及在访问
期间发生的事情,因此它似乎是一个重要的领域概念。
我想你也会有一个日托中心
,所有受照顾的儿童都会参加
因此,我将使用这个聚合根:
- 日托中心
- 孩子
- 拜访
顺便说一句:我看到另一个不变量:
“儿童不能同时在多个日托中心”
“多次点击“登录”按钮”
如果每个命令都有一个唯一的id,该id是为每个有意的尝试生成的,而不是由每次单击(无意的)生成的,则可以缓冲最后n个接收到的命令id并忽略重复项
或者,您的消息传递基础设施(服务总线)可以为您处理这些问题
创建访问
由于您使用多个聚合,因此必须查询一些(可靠、一致的)存储,以确定不变量是否满足要求。
(或者,如果冲突很少发生,并且手动“取消”无效的访问是合理的,那么最终的一致读取模型也会起作用……)
由于子项
只能有一次当前的访问
,因此子项
仅存储上次开始的访问
的少量信息(事件)
无论何时开始新的访问
,都会查询“真相来源”(写入模型)以查找任何先前的访问
,并检查访问
是否已结束
(另一种选择是,访问
只能通过子项
聚合来结束,在子项
中再次存储一个“结束”-事件,但这对我来说感觉不太好……但这只是个人意见)
查询(验证)部分可以通过一个特殊的服务来完成,或者只需将一个存储库传递给该方法并直接在那里查询即可——这次我选择第二个选项
下面是一些C#ish brain编译的伪代码,以表达我认为您可以如何处理它:
公共类DayCareCenterId
{
公共字符串值{get;set;}
}
公营日托中心
{
公共DayCareCenter(DayCareCenterId,字符串名称)
{
RaiseEvent(创建的新DayCareCenter(id、名称));
}
私有无效应用(DayCareCenterCreated@event)
{
//...
}
}
公共类访问
{
公共字符串值{get;set;}
}
公开课堂参观
{
公众访问(访问id、儿童id、日期时间开始)
{
RaiseEvent(创建的新访问(id、childId、start));
}
私有无效应用(VisitCreated@event)
{
//...
}
公众访问()
{
RaiseEvent(新访问者(id));
}
私有无效应用(Visited@event)
{
//...
}
}
公营儿童
{
公共字符串值{get;
Greg Young:
The big mental leap in this kind of system is to realize that
you are not the book of record. In the warehouse example the
*warehouse* is the book of record. The job of the computer
system is to produce exception reports and estimates of what
is in the warehouse