在 CQRS/Eventsourcing 中,哪个是 parent 修改所有 children 状态的最佳方法?

In CQRS/Eventsourcing which is best approach for a parent to modify the state of all it's children ??

用例:假设我有以下聚合

问题:如何向所有 10 个 CustomerAggregate 发送 DisableCustomer 命令以更新它们的状态以禁用?

customerState.enabled = false

解决方案:由于CQRS不允许写入端查询读取端来获取CustomerAggregate ID列表我想到了以下几点:

  1. CustomerRootAggregate 始终将其所有 CustomerAggregate 的 ID 作为 json 存储在数据库中。当 CustomerRootAggregate 收到 DisableAllCustomers 的命令时,它将获取 CustomerIds json 并向所有 children 发送 DisableCustomer 命令,其中每个 child 将在应用 DisableCustomer 命令之前恢复它的状态。但这意味着我必须保持 CustomerIds json 记录的一致性。
  2. 客户端(浏览器 - UI)应始终发送要应用 DisableCustomer 的 CustomerId 列表。但这对于拥有数千个客户的数据库来说会有问题。
  3. 在 REST API 层中检查命令 DisableAllCustomers 并从读取端获取所有 ID 并将填充了 ID 的 DisableAllCustomers(ids) 发送到写入端。

哪种方法是推荐的或更好的方法?

  • Root aggregate - CustomerRootAggregate (manages each CustomerAggregate)
  • Child aggregate of the Root aggregate - CustomerAggregate (there are 10 customers)

对于初学者来说,语言 "child aggregate" 有点令人困惑。您的模型包含一个 "parent" 实体,该实体持有对 "child" 实体的直接引用,那么这两个实体必须是同一聚合的一部分。

但是,您可能有每个客户的 Customer 聚合,以及管理 Id 集合的 CustomerSet 聚合。

How do I send DisableCustomer command to all the 10 CustomerAggregate to update their state to be disabled ?

通常的答案是您 运行 查询以获取要禁用的客户集,然后向每个客户发送 disableCustomer 命令。

所以 3 和 2 都是合理的答案,但需要注意的是,如果某些 DisableCustomer 命令失败,您需要考虑您的要求。

2 特别有吸引力,因为它清楚地表明客户端(操作员)正在描述一项任务,然后应用程序通过领域模型 运行 将其转换为命令。

尝试将 "thousands" 的客户 ID 打包到消息中可能是一个问题,但对于一些用例,您可以找到一种方法来缩小它。例如,如果任务是 "disable all",则客户端可以向应用程序发送有关如何重新创建 "all" 集合的说明——即:"run this query against this specific version of the collection" 明确描述了要禁用的客户列表.

When a Command for DisableAllCustomers is received by CustomerRootAggregate it will fetch the CustomerIds json and send DisableCustomer command to all the children where each child will restore it's state before applying DisableCustomer command. But this means I will have to maintain CustomerIds json record's consistency.

这接近正确的想法,但还不完全正确。您向集合聚合发送命令。如果它接受命令,它会生成一个事件,描述要禁用的客户 ID。此域事件作为集合聚合的事件流的一部分保留。

使用负责创建 进程管理器 的事件处理程序订阅这些事件。这个流程管理器是另一个事件源状态机。它看起来有点像聚合,但它响应事件。当一个事件传递给它时,它会更新自己的状态,将这些事件保存在当前事务中,然后将命令调度到每个客户聚合。

但是这样做需要做很多额外的工作。传统观点建议您通常应该首先假设流程管理器方法不是必需的,并且仅在业务需要时才引入它。 "Premature automation is the root of all evil" 或类似的东西。