以正确的方式实施 CQRS / ES

Posted

技术标签:

【中文标题】以正确的方式实施 CQRS / ES【英文标题】:Implementing CQRS / ES the proper way 【发布时间】:2021-11-02 15:35:17 【问题描述】:

最近我期待在我的微服务中实现带有事件溯源的 CQRS / ES 模式。

我一直在阅读这些模式,但我有一些问题在任何地方都找不到答案:

    做CQRS/ES时,每个微服务是否应该有自己的本地 不再使用数据库(在微服务中)?

    我知道会有一个用于写入的事件存储和一个只读投影数据库,我完全理解它们的目的,但是微服务是否需要 出于任何原因他们自己的本地数据库? (优点/缺点)

    示例:订单微服务可以有本地订单数据库、项目服务、项目本地数据库等...除了实现的事件源数据库和预测数据库。

    之前如何验证微服务中是否存在某些数据 实际发出命令?

    假设我想下一个新订单,所以我首先假设我必须 检查该项目是否仍有库存,然后执行另一个 操作/秒。

    但是,如果我想检查某件商品是否仍有库存,我该去哪里 查询该数据,它将是投影(只读)数据库,还是 每个微服务都有一个本地数据库?

此时我已经阅读了很多关于 CQRS / ES 的文章,但其中大多数只是解释了这个概念,而不是真正深入到现实生活场景/解释如何实现它。如果您有任何建议,我将不胜感激。

非常感谢

【问题讨论】:

【参考方案1】:

一般来说,在处理微服务时,建议(无论您是否使用 CQRS/ES)不要让两个微服务使用同一个数据库,或者至少不要让两个微服务写入同一个数据库数据库。这允许每个微服务控制其架构,仅在微服务需要时才需要更改。这样做的另一个优点是数据库完全封装在服务中:它纯粹是一个实现细节。

实现读取模型的微服务完全有可能没有数据库:它可能能够将所有状态保存在内存中(一个示例可能是为您的监控基础架构公开指标的读取模型),或者可能只是将事件从写入模型转换为命令到另一个服务(因此它的所有状态只是它在事件流中的位置)。

如果我想检查一个项目是否仍有库存,我在哪里查询该数据,是投影(只读)数据库,还是每个微服务拥有的本地数据库?

在事件源系统中,不是事件流的每个视图都是一个投影。因此,根据您的要求,您的服务可以查询另一个服务或根据事件维护自己的视图。

请注意,在任何给定时刻,可能存在已发布到事件流的事件(即它已无可争议地发生)但也存在未处理该事件的投影:投影最终与事件流。因此,对某件商品是否有货的任何检查都只会告诉您该商品在过去的某个时间点有货(没关系,以 Greg Young 为例,没有库存数据可以保证没有任何东西被盗仓库,除非小偷碰巧有礼貌地更新计数,因为他们带着他们的战利品走出去)。在您查询后的纳秒内,它可能会在您下订单之前收到导致其缺货的事件的消息。

因此,如果商品没有库存,可能值得发送命令并让它拒绝您的订单。写端(这是系统中一致性更强的部分,尽管应该记住,在许多情况下,一个组件的事件是另一个组件的命令)没有义务接受每个命令;在这种情况下,“命令”实际上意味着“将事件发布到事件流中的礼貌请求,这些事件符合我想要的宇宙状态”。

【讨论】:

感谢您的解释!

以上是关于以正确的方式实施 CQRS / ES的主要内容,如果未能解决你的问题,请参考以下文章

如何在 cqrs 上下文中正确处理日志记录?

与 DDD + CQRS + ES 的并发

带有 NotificationCompat.Builder 的 Android 状态栏通知。正确的实施方式?

CQRS+ES项目解析01-Diary.CQRS

为 CQRS 实施包装 Masstransit 是一个好习惯吗? [关闭]

AUTO_INCREMENT 在我的情况下是不是正确实施?