【发布时间】:2020-11-18 13:40:23
【问题描述】:
我正在构建一个用于管理健康诊所的应用程序。
我们在安排约会时发现了一个竞争条件案例,直到现在,团队成员都没有找到解决方案。
安排约会时,需要验证一些业务规则:
- 不能与同一医生或同一患者安排在同一时间
- 医生一个月只能参加N次预约
- 一周内,医生只能参加N次预约
因此,我们认为的第一种方法是创建一个聚合来保存所有约会,并负责安排它们,但是这个聚合会很大并且在技术上是不可接受的。
第二种方法,也是目前的方法,是将 Appointment 创建为 Aggregate Root,然后使用查询读取端的域服务(域层中的接口和基础层中的实现)对其进行验证。
今天的样子:
-
在命令处理程序中,实例化新的 Appointment,在其构造函数中传递一个域服务
-
约会调用域服务,它查询读取端并验证规则。但是,这里可能会出现竞争条件(同时安排两个约会,因为两者看不到对方,所以都将被创建)。
-
如果域服务验证规则,则创建约会,但状态为 PENDING,并触发域事件 AppointmentRequested。
-
在读取端,订阅了此事件,并且在读取的数据库中插入了一个投影(状态 = PENDING)。在同一个事务中,一个命令 CompleteAppointmentSchedule 被插入到我的发件箱中,并很快被写入方异步发送和接收。
-
写端处理调用约会.CompleteSchedule(domainService) 的命令。实例化新约会时传递的相同域服务再次传递给约会。但是,现在,约会已经在读取数据库中,并且可以检查业务规则。
以这种方式使用读取端是否正确?如果不使用读取端,我们无法想出另一种方法来检查此规则。一位团队成员建议我们可以为我们的写入端创建一个私有读取端,并在这些情况下使用它而不是读取端,但是,当我们使用 EventStore DB 时,我们将不得不创建另一个数据库,如我们在读取端 (pgsql) 上使用,以便能够在这个私有读取端这样做。
【问题讨论】:
-
您可以使用 SAGA 进行管理。发布了两个 AppointmentCreated 事件,那么你需要更新读取端,但是在读取端,你有唯一的约束,所以其中一个无法保存,然后发布另一个事件以取消第二个 AppointmentCreated 事件
标签: domain-driven-design race-condition cqrs event-sourcing