交叉聚合引用:命令处理程序可以在聚合 ID 集合之间循环,还是事件处理程序可以分派命令?
Cross aggregate references: Can a command handler loop among a collection of aggregate ids, or can event handlers dispatch commands?
我需要从多个集合持有的集合中删除一个 id。
假设我有一个 EmployeeAggregate,其中包含一个 Hobbies'id 集合。
聚合是事件来源的。
让我们说在应用程序的某个地方,某人在一个处理爱好的基本 crud 应用程序中 table,删除了一个爱好行。我怎样才能反映所有 EmployeeAggregates 的变化?
现在在事件存储中我有关于 EmployeeAggregate 的事件,关于 Hobbies 的事件(使用 HobbieWasDeletedEvent),但是没有什么可以让 EmployeeAggregate 处理这个 HobbieWasDeletedEvent。
我能做什么的一些想法:
解决方案 1:我没有为 DeleteHobby 发送命令,而是遍历所有 Hobbies->getEmployee 并为每个匹配发送命令,由 EmployeeAgregate 处理。缺点:如果数据很大怎么办?循环可能永远不会结束
解决方案 2:我只发送一个包含所有 EmployeId 数组的命令。然后我在命令处理程序中执行我的循环,为每次迭代重构聚合并调用 remove hobby 方法。我知道理论上一个命令 => 一个聚合,但我们是在谈论一个命令 = 一个聚合 TYPE 还是一个聚合实例?我同意 RemoveHobbyFromEmployeeCommandHandler 不能对员工以外的其他事物采取行动;但它可以作用于一组相同类型的员工吗?
解决方案 3:我执行解决方案一(或解决方案二),但采用命令采购方式:我不是同步分派命令,而是将其传递到异步命令总线并让工作人员出队它们并将它们传递给处理程序。火了就不管了。
解决方案 4:我不应该关心通知集合爱好已被删除,因为这不是域问题。如果业务团队需要知道这一点,我将在投影端进行检查,以确保集合的所有爱好 ID 在写入读取模型之前都存在。缺点:如果我即时重组我的 EmployeeAggregate 只是为了 "dump" 它并显示它的值,它的爱好集合中仍然会有已删除的爱好 ID。所以它不会代表现实。但这真的是一个用例吗?
解决方案 5:Sagas 在这里有用吗?
还有其他想法吗?
[编辑]
- 受 Dnomya 评论的启发,我想到了另一个想法(见下文):通过 Employee 事件处理程序,我编写了一个 "local" readmodel。它只是一个 table 跟踪所有 EmployeeId / HobbyId 关联,并且仅由某些特定事件(那些关注 employee/hobby 关系的事件)更新。然后,当处理 HobbyWasRemoved 时,我从这个本地读取模型中获取所有 EmployeeIds 并做一些事情。但是什么?为找到的每个 EmployeeId 派发命令?事件处理程序可以这样做吗?这不是 saga 所做的事情吗?但是,如果这个员工 ID 集合很大怎么办?
一种方法是使用 saga/process 模拟两阶段提交来管理以下过程:
分配爱好
- 用户向 Employee 集合发出 AssignHobby 命令。引发 HobbyAssigned 事件。
- Saga 收到 HobbyAssigned 并向 Hobby 集合发出 AddEmployee 命令。
- 如果 Hobby 处于活动状态,则会引发 EmployeeAdded 事件
- 如果 Hobby 处于非活动状态,则会引发 EmployeeNotAdded 事件
- Saga 接收到 EmployeeNotAdded 事件并向 Employee 集合发出 RemoveHobby 命令。引发 HobbyRemoved 事件。
- 根据不让员工有闲置爱好的重要性,您也可以在此步骤中引入两阶段提交,例如首先请求添加 Hobby,一旦 Hobby 批准,您就可以与 Employee 完成请求。
删除爱好
- 用户向 Hobby 集合发出停用命令。引发停用事件。
- 从现在开始,Hobby 聚合将被标记为不活动。任何添加员工的请求都将导致 EmployeeNotAdded 事件。
- Saga 收到 Deactivated 事件。它加载 Hobby 聚合并向分配给 Hobby 的每个 Employee 聚合发出 RemoveHobby 命令。每个都会引发一个 HobbyRemoved 命令。
我需要从多个集合持有的集合中删除一个 id。 假设我有一个 EmployeeAggregate,其中包含一个 Hobbies'id 集合。 聚合是事件来源的。
让我们说在应用程序的某个地方,某人在一个处理爱好的基本 crud 应用程序中 table,删除了一个爱好行。我怎样才能反映所有 EmployeeAggregates 的变化?
现在在事件存储中我有关于 EmployeeAggregate 的事件,关于 Hobbies 的事件(使用 HobbieWasDeletedEvent),但是没有什么可以让 EmployeeAggregate 处理这个 HobbieWasDeletedEvent。
我能做什么的一些想法:
解决方案 1:我没有为 DeleteHobby 发送命令,而是遍历所有 Hobbies->getEmployee 并为每个匹配发送命令,由 EmployeeAgregate 处理。缺点:如果数据很大怎么办?循环可能永远不会结束
解决方案 2:我只发送一个包含所有 EmployeId 数组的命令。然后我在命令处理程序中执行我的循环,为每次迭代重构聚合并调用 remove hobby 方法。我知道理论上一个命令 => 一个聚合,但我们是在谈论一个命令 = 一个聚合 TYPE 还是一个聚合实例?我同意 RemoveHobbyFromEmployeeCommandHandler 不能对员工以外的其他事物采取行动;但它可以作用于一组相同类型的员工吗?
解决方案 3:我执行解决方案一(或解决方案二),但采用命令采购方式:我不是同步分派命令,而是将其传递到异步命令总线并让工作人员出队它们并将它们传递给处理程序。火了就不管了。
解决方案 4:我不应该关心通知集合爱好已被删除,因为这不是域问题。如果业务团队需要知道这一点,我将在投影端进行检查,以确保集合的所有爱好 ID 在写入读取模型之前都存在。缺点:如果我即时重组我的 EmployeeAggregate 只是为了 "dump" 它并显示它的值,它的爱好集合中仍然会有已删除的爱好 ID。所以它不会代表现实。但这真的是一个用例吗?
解决方案 5:Sagas 在这里有用吗?
还有其他想法吗?
[编辑]
- 受 Dnomya 评论的启发,我想到了另一个想法(见下文):通过 Employee 事件处理程序,我编写了一个 "local" readmodel。它只是一个 table 跟踪所有 EmployeeId / HobbyId 关联,并且仅由某些特定事件(那些关注 employee/hobby 关系的事件)更新。然后,当处理 HobbyWasRemoved 时,我从这个本地读取模型中获取所有 EmployeeIds 并做一些事情。但是什么?为找到的每个 EmployeeId 派发命令?事件处理程序可以这样做吗?这不是 saga 所做的事情吗?但是,如果这个员工 ID 集合很大怎么办?
一种方法是使用 saga/process 模拟两阶段提交来管理以下过程:
分配爱好
- 用户向 Employee 集合发出 AssignHobby 命令。引发 HobbyAssigned 事件。
- Saga 收到 HobbyAssigned 并向 Hobby 集合发出 AddEmployee 命令。
- 如果 Hobby 处于活动状态,则会引发 EmployeeAdded 事件
- 如果 Hobby 处于非活动状态,则会引发 EmployeeNotAdded 事件
- Saga 接收到 EmployeeNotAdded 事件并向 Employee 集合发出 RemoveHobby 命令。引发 HobbyRemoved 事件。
- 根据不让员工有闲置爱好的重要性,您也可以在此步骤中引入两阶段提交,例如首先请求添加 Hobby,一旦 Hobby 批准,您就可以与 Employee 完成请求。
删除爱好
- 用户向 Hobby 集合发出停用命令。引发停用事件。
- 从现在开始,Hobby 聚合将被标记为不活动。任何添加员工的请求都将导致 EmployeeNotAdded 事件。
- Saga 收到 Deactivated 事件。它加载 Hobby 聚合并向分配给 Hobby 的每个 Employee 聚合发出 RemoveHobby 命令。每个都会引发一个 HobbyRemoved 命令。