术语re-hydrated是指从数据库中反序列化聚合实例的过程。 Jana(软件架构师)发言:
你可以考虑使用Memento模式来处理持久化和rehydration。
团队考虑的第二种方法(如下图所示)是使用单个聚合来代替两个聚合。
图中的数字对应于以下步骤:
从UI发送一个命令用来注册参会者X和Y到157号会议,这个命令被路由到会议(Conference)聚合,聚合ID为157。
ID为157的会议(Conference)聚合从数据库中取回还原(rehydrated)。
订单(Order)实体将校验本次预订(它将查询可用座位(SeatsAvailability)实体以查看是否还有足够的座位),然后调用方法更新在会议(Conference)实体上预订的座位数量。
可用座位(SeatsAvailability)实体更新自己已预订的座位总数。
更新后的会议(Conference)聚合的被持久化到数据库中。
团队考虑的第三种方法(如下图所示)是使用流程管理器来协调两个聚合之间的交互。
图中的数字对应于以下步骤:
从UI发送一个命令用来注册参会者X和Y到157号会议,这个命令被路由到订单(Order)聚合。
这个新的订单(Order)聚合,ID为4239,被持久化到数据库中
订单(Order)聚合引发(Raise)一个事件,这个事件将被RegistrationProcessManager类处理
RegistrationProcessManager类将发送一个命令到ID为157的可用座位(SeatsAvailability)聚合
这个可用座位(SeatsAvailability)聚合从数据库中取回还原(rehydrated)
可用座位(SeatsAvailability)聚合更新自己的预定座位总数,然后持久化回数据库
Gary(CQRS专家)发言:流程管理器或Saga,起初,团队将RegistrationProcessManager类看做一个Saga,但是,当他们重新阅读Hector Garcia-Molina和Kenneth Salem合著的《Saga》一文中对“Saga”的最初定义后,他们修改了自己的决定。主要原因是预定流程并不包含明确的补偿步骤,所以并不需要一个长生命周期的事务。
有关流程管理器和Saga的更多信息,请参见参考指南中的第6章“A Saga on Sagas”
团队还明确了下列问题:
在哪里验证是否有足够的座位可供注册?在订单(Order)聚合里还是可用座位(SeatsAvailability)聚合里?
事务边界在哪里?
当多个注册者试图同时下订单时,该模型如何处理并发问题?
聚合根是什么?
验证在登记人可以预订座位之前,系统必须检查是否有足够的座位。虽然UI中的逻辑可以在发送命令之前验证是否有足够的可用座位,但是领域中的业务逻辑也必须执行检查。这是因为在UI执行验证之后到系统将命令发送到领域中的聚合时,状态可能会发生变化。
Jana(软件架构师)发言:当我们在这里谈UI验证时,我们指的是模型-视图-控制器(MVC)执行的验证,而不是浏览器前端。
在第一个模型中,验证要么在订单(Order)聚合里,要么在可用座位(SeatsAvailability)聚合里。如果是前者,则订单(Order)聚合必须在预订之前和引发事件之前从可用座位(SeatsAvailability)聚合中检查当前的座位可用性。如果是后者,那么可用座位(SeatsAvailability)聚合必须以某种方式通知订单(Order)聚合它不能预订座位,并且订单(Order)聚合必须撤消(或弥补)它迄今为止完成的任何工作。
Beth(业务经理)发言:撤销只是现实生活中发生的许多弥补操作之一,弥补操作不仅仅局限于系统内,甚至可以是系统外的人工操作,例如:一个Contoso的职员或客户经理打电话给注册者们,告诉他们系统发生了一个错误,请他们忽略Contoso发来的最终确认邮件。
第二个模型的行为类似,除了订单(Order)聚合和可用座位(SeatsAvailability)聚合是在会议(Conference)聚合里协作的。
在第三个模型中,使用了流程管理器,聚合通过流程管理器互相传递关于注册者是否可以在当前时间进行预订的消息。
所有这三个模型都需要实体就验证过程进行通信,但是与流程管理器进行通信的第三个模型看起来比其他两个模型更复杂一些。
事务边界