【发布时间】:2018-05-13 05:54:59
【问题描述】:
假设我们有以下内容:
DDD聚合A和B,A可以引用B。
一个管理 A 的微服务,它公开以下命令:
- 创建一个
- 删除A
- 将 A 链接到 B
- 取消 A 与 B 的链接
一个微服务管理 B,它公开以下命令:
- 创建 B
- 删除B
成功的创建、删除、链接或取消链接总是会导致执行该操作的微服务发出相应的事件。
为这两个微服务设计事件驱动架构的最佳方法是:
- A 和 B 最终将始终保持一致。通过一致性,我的意思是如果 B 不存在,则 A 不应引用 B。
- 来自两个微服务的事件可以轻松地投射到单独的读取模型中,在该模型上可以进行跨越 A 和 B 的查询
具体来说,以下示例可能会导致暂时的不一致状态,但在所有情况下都必须最终恢复一致性:
示例 1
- 初始一致状态:A 存在,B 不存在,A 未链接到 B
- 命令:将 A 链接到 B
示例 2
- 初始一致状态:A存在,B存在,A链接到B
- 命令:删除B
示例 3
- 初始一致状态:A存在,B存在,A未链接到B
- 同时执行两个命令:将 A 链接到 B 并删除 B
我有两个解决方案。
解决方案 1
- 微服务 A 仅允许将 A 链接到 B,前提是它先前已收到“B 创建”事件且未收到“B 已删除”事件。
- 仅当微服务 B 之前未收到“A 链接到 B”事件,或者该事件之后出现“A 与 B 未链接”事件时,微服务 B 才允许删除 B。
- 微服务 A 侦听“B 已删除”事件,并在接收到此类事件后,将 A 与 B 取消链接(针对 B 在收到链接到 B 事件的 A 之前被删除的竞争条件)。
解决方案 2:
- 微服务 A 始终允许将 A 链接到 B。
- 微服务 B 侦听“A 链接到 B”事件,并在收到此类事件后验证 B 是否存在。如果没有,它会发出“拒绝 B 的链接”事件。
- 微服务 A 侦听“B 已删除”和“链接到 B 被拒绝”事件,并在收到此类事件后,将 A 与 B 取消链接。
编辑:Guillaume 提出的解决方案 3:
- 微服务 A 仅允许在之前未收到“B 已删除”事件的情况下将 A 链接到 B。
- 微服务 B 始终允许删除 B。
- 微服务 A 侦听“B 已删除”事件,并在收到此类事件后,将 A 与 B 取消链接。
我看到的解决方案 2 的优势是微服务不需要跟踪其他服务发出的过去事件。在解决方案 1 中,基本上每个微服务都必须维护另一个微服务的读取模型。
解决方案 2 的一个潜在缺点可能是在读取模型中投射这些事件会增加复杂性,特别是如果将更多遵循相同模式的微服务和聚合添加到系统中。
一种或另一种解决方案是否有其他(不利)优势,甚至是我不知道的反模式,应该不惜一切代价避免? 有比我建议的两个更好的解决方案吗?
任何建议将不胜感激。
【问题讨论】:
标签: domain-driven-design microservices cqrs