使用事件溯源和 CQRS 有什么缺点?

What are the disadvantages of using Event sourcing and CQRS?

事件溯源和 CQRS 很棒,因为它让开发人员摆脱了一个预建模数据库的束缚,除非有大数据迁移项目,否则开发人员必须在应用程序的整个生命周期中使用该数据库。 CQRS 和 ES 还有其他好处,例如扩展事件存储、审计日志等,这些已经在互联网上广为流传。

但是缺点是什么?

以下是我在研究和编写小型演示应用程序后能想到的一些缺点

  1. 复杂:有人说ES很复杂。但我想说拥有一个复杂的应用程序比一个复杂的数据库模型要好,在复杂的数据库模型上你只能 运行 使用查询语言(多连接、索引等)进行非常有限的查询。我的意思是像 Scala 这样的一些编程语言有非常丰富的集合库,可以非常灵活地生成一些非常复杂的聚合,还有 Apache Spark 可以很容易地查询分布式集合。但是数据库总是受限于它的查询语言能力,分布式数据库比分布式应用程序代码更难(只需在另一台机器上部署另一个实例!)
  2. 高磁盘space 使用:事件存储可能最终会使用大量磁盘space 来存储事件。但是我们可以安排每隔几周清理一次并创建快照,也许我们可以将历史事件本地存储在外部 HD 上,以防我们将来需要旧事件?
  3. 高内存使用率:每个域对象的状态都存储在内存中,这可能会增加 RAM 使用率,我们都知道 RAM 是多么昂贵。 大问题!!因为我很穷!有什么解决办法吗?可以使用 Sqlite 而不是将状态存储在内存中吗?我是否通过在我的应用程序中引入多个 Sqlite 实例使事情变得更复杂?
  4. 更长的启动时间:失败或软件升级启动速度慢,具体取决于事件的数量。但是我们可以使用快照来解决这个问题?
  5. 最终一致性:某些应用程序的问题。想象一下,如果 Facebook 使用带有 CQRS 的事件源来存储 posts 并考虑到 facebook 的系统有多忙,如果我 post 编辑了一个 post 我会看到我的 fb post 下一个天:)
  6. 事件存储中的序列化事件:事件将存储事件存储为序列化对象,这意味着我们无法查询事件存储中的事件内容,这是不鼓励的。以后我们将无法为该事件添加其他属性。解决方案是将事件存储为 JSON 对象而不是序列化事件?但这是个好主意吗?或添加更多事件以支持对原始事件对象的更改?

有人可以评论我在这里提出的缺点,如果我错了请纠正我,并提出我可能遗漏的任何其他建议吗?

这是我的看法。

  1. CQRS + ES 可以通过丰富的域对象、简单的数据模型、历史跟踪、对并发问题的更多可见性、可扩展性等,使复杂软件系统中的事情变得简单得多。它确实需要以不同的方式思考系统,因此可能很难找到合格的开发人员。但是 CQRS 使得开发人员之间的职责分离变得更加简单。例如,初级开发人员可以完全使用读取端工作,而无需触及业务逻辑。

  2. 数据副本肯定需要更多磁盘space。但是现在存储相对便宜。它可能需要 IT 支持团队做更多的备份,并计划在出现​​问题的情况下如何恢复系统。然而,如今的服务器虚拟化使其成为一个更加精简的工作流程。此外,在没有单一数据库的情况下,在系统中创建冗余要容易得多。

  3. 我不认为较高的内存使用量是个问题。业务对象水化应按需进行。对象不应保留对已持久化事件的引用。事件水合应该只在持久化数据时发生。在读取方面,您没有实体 -> DTO -> ViewModel 转换,这通常发生在分层系统中,并且您不会有全功能 ORM 通常执行的任何类型的对象更改跟踪。大多数系统执行的读操作明显多于写操作。

  4. 如果由于各种数据上下文的初始化而使用多个异构数据库,较长的启动时间可能是一个小问题。但是,如果您使用像 ADO .NET 这样简单的东西来与事件存储和读取端的微型 ORM 交互,系统将 "cold start" 比任何功能齐全的 ORM 都快。这里重要的是不要使访问数据的方式过于复杂。这实际上是 CQRS 应该解决的问题。正如我之前所说,读取端应该为视图建模,并且没有任何重新映射数据的开销。

  5. 根据我的经验,两阶段提交适用于不需要为数千用户扩展的系统。您需要选择能够与分布式事务协调器配合良好的数据库。例如,PostgreSQL 可以很好地用于读写分离模型。如果系统需要针对大量并发用户进行扩展,则必须在设计时考虑到最终的一致性。在某些情况下,您的聚合根或上下文边界不使用 CQRS 来避免最终一致性。这对域的非协作部分有意义。

  6. 如果您为事件存储选择了正确的数据库,则可以查询 JSON 或 XML 等格式的序列化事件。这应该只用于分析目的。除了聚合根 ID 和事件类型之外,系统内部的任何内容都不应通过任何其他方式查询事件存储。该数据将被编入索引并存在于序列化事件之外。

只是对第 5 点发表评论。我听说 Facebook 确实使用具有最终一致性的 ES,这就是为什么您有时会看到 post 在 [=17= 之后消失并重新出现的原因]编辑它。

通常您的浏览器访问的读取模型位于 'close' 给您,但是在您创建 post 之后,SPA 切换到接近您写入的读取模型-模型。写入模型(事件)和读取模型之间的接近意味着您可以看到自己的 post.

但是,15 分钟后,您的 SPA 切换回第一个更近的读取模型。如果包含您的 post 的事件尚未传播到该读取模型,您将看到您自己的 post 消失,但稍后会重新出现。

我知道问这个问题已经快 3 年了,但 this article 可能对某些人有用。重点是

  • 使用快照缩放
  • 数据可见性
  • 架构更改
  • 处理复杂的域
  • 需要向大多数新团队成员解释

Event sourcing and CQRS is great because it gets rids developers being stuck with one pre-modeled database which the developer has to work with for the lifetime of the application unless there is a big data migration project.

这是一个很大的误解。关系数据库正是为模型的发展而发明的(由于简单的二维表而不是预定义的层次结构)。通过确保数据访问封装的视图和过程,逻辑和物理模型可以独立发展。这也是为什么SQL用同一种语言定义DDL和DML的原因。一些 RDBMS 还允许对所有这些演变进行版本控制和在线部署(持续交付)作为基于 Oracle 版本的重新定义。

大数据结构是预定义的,只能使用为此结构开发的代码来读取。可以立即使用,但如果没有确切的版本、语言编译器或解释器,10 年后你将很难阅读它。

我希望不要迟到尝试给出答案。在这几个月里,我对这个论点做了很多研究,目的是为我的体系结构的某些部分实施一个 production-grade 解决方案,其中 ES 是有意义的

复杂:其实,它不应该很复杂,它的使命是简单得要命。如何?将所有复杂性从业务逻辑代码推向基础设施代码。数据访问应该由还不够成熟的框架来完成。不过,在 ES/CQRS 比赛中没有明显的赢家,可能是因为仍然是 niche/hipster 方法(?)所以一些团队正在推出自己的解决方案或采用一些 ready-made 技术,例如 Axon

高磁盘space使用:我会说更多,我会说*可能无限*磁盘使用。但是如果你转向 ES,你也有很好的理由容忍这个明显的缺点。让我们给其中一些:

  1. Audit Logs : 数据存储是一个事件日志,我们已经知道了。财务应用程序或每个 mission/safety 关键应用程序可能需要一个集中的审计日志,以便能够说明 WhoWhich 中做了 What时刻。 ES 提供了盒子的这种功能……您还可以使用一些对业务有意义的元数据来装饰您的事件条目(例如,与某些 API 消费者身份相关的事务 ID、操作的严重性级别……)

  2. 高并发:有些系统的逻辑资源状态被许多客户端以并发方式改变。这些是游戏、物联网平台等。记录事件而不是更改状态表示可能是提供事件总顺序的明智方法。另一种方法是将同步内容委托给 DB。但如果你喜欢 ES

    ,这不是你想要的
  3. Analytics 假设您拥有大量具有大量商业价值的数据,但您仍然不知道是哪一个。多年来,我们通过转换具有不同信息模型(OLAP 多维数据集)的数据组织,从应用程序信息中提取知识。事件存储再次提供类似的开箱即用的东西。事件日志是最原始的信息表示形式,您可以通过多种方式处理它们,批量处理或对存储的事件做出反应

高内存占用:我认为一旦你建立了你的投影应该是一样的

更长的启动时间:如果读取端缓存其投影并“记住”最后更新事件,它不应该re-apply整个事件序列。快照是一种缓解措施,但如果你做了很多快照,那么你可能在 ES 上做出了错误的选择。我认为这个问题在微服务生态系统中是次要的,在微服务生态系统中,可以在不中断服务的情况下屏蔽启动时间。事实上,当您应用微服务时,您可以充分利用 ES/CQRS

最终一致性:这要怪 CAP 定理,而不是 ES。许多非 ES/CQRS 必须处理这个问题,但在很多情况下这不是真正的问题。这些是 ES 非常适合的场景。并且您可以将 ES 和非 ES 服务混合到同一个平台

事件存储中的序列化事件:如果 non-serialized 事件表示很重要,您可以使用 document-oriented 数据库,但如果您这样做这是为了查询事件负载,你错过了 ES/CQRS 的要点。 ES的意思是把所有的数据操作从DB端移到应用层,每一块都变化很快,而且都是无状态的。这增强了可扩展性和容错性,并提供了塑造团队组织的方法,例如让前端 guy/girl 轻松地在 javascript 中写入 his/her BFF。

我希望将这些原则付诸实践并取得良好的效果,并从这种激动人心的方法中受益