使用事件采购和CQRS有哪些缺点?

时间:2015-10-22 11:14:07

标签: cqrs event-sourcing

事件源和CQRS非常棒,因为它让开发人员陷入了一个预建模型数据库,开发人员必须在应用程序的生命周期内使用该数据库,除非有大数据迁移项目。 CQRS和ES还有其他好处,如扩展事件存储,审计日志等已经遍布互联网。

但有哪些缺点?

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

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

5 个答案:

答案 0 :(得分:32)

以下是我对此的看法。

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

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

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

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

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

  6. 如果为事件存储选择正确的数据库,则可以按序列化格式(如JSON或XML)查询事件。这应该仅用于分析目的。系统内部的任何内容都不应该通过聚合根id和事件类型之外的任何内容来查询事件存储。该数据将被索引并存在于序列化事件之外。

答案 1 :(得分:5)

只是评论第5点。我被告知Facebook确实使用ES与最终一致性,这就是为什么你有时会看到一个帖子消失并在你发布后重新出现。

通常,您的浏览器访问的读取模型位于“靠近”位置,但在您发布帖子后,SPA会切换到与您的写入模型接近的读取模型。写模型(事件)和读模型之间的紧密接近意味着您可以看到自己的帖子。

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

答案 2 :(得分:1)

  

事件源和CQRS很棒,因为它使开发人员摆脱了一个预先建模的数据库的困扰,除非存在大型数据迁移项目,否则开发人员必须在应用程序的整个生命周期内使用该数据库。

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

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

答案 3 :(得分:0)

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

  • 通过快照扩展
  • 数据可见性
  • 更改架构
  • 处理复杂的域
  • 需要向大多数新团队成员进行解释

答案 4 :(得分:0)

我希望不要晚于尝试给出答案。在这几个月中,我对该参数进行了很多研究,目的是为我的体系结构中某些对ES有意义的部分实施产品级解决方案

复杂:实际上,它不应该复杂,它的任务是致命的简单。怎么样?将所有复杂性从业务逻辑代码扩展到基础结构代码。数据访问应由尚未成熟的框架完成。 ES / CQRS竞赛中仍然没有明确的赢家,也许是因为它仍然是利基/时髦方法(?),因此,一些团队正在推出自己的解决方案或采用一些现成的技术,例如Axon

高磁盘空间使用量:我会说更多,我会说*潜在无限*磁盘使用率。但是,如果您选择使用ES,您也有充分的理由容忍这种明显的漂移。让我们给一些:

  1. 审核日志:数据存储区是事件日志,我们已经知道了。财务应用程序或每个关键任务/安全性至关重要的人员,可能需要集中审核日志,以说明哪个时刻做出了什么。 ES提供了这种开箱即用的功能...您还可以使用一些具有业务可预测性的元数据来装饰事件条目(例如,与某些API使用者身份相关联的交易ID,操作的严重性级别...)

  2. 高并发性:在某些系统中,许多客户端以并行方式更改逻辑资源状态。这些是游戏,物联网平台等。记录事件而不是更改状态表示可能是提供事件总顺序的明智方法。另一种方法是将同步化内容委托给DB。但这不是您想要的ES

  3. Analytics(分析)假设您拥有大量具有大量业务价值的数据,但您仍然不知道哪个。多年来,我们通过将数据组织与不同的信息模型(OLAP多维数据集)进行转换,从应用程序信息中提取知识。事件存储再次提供了类似的功能。事件日志是最原始的信息表示形式,您可以采用多种方式来批量处理或对存储的事件做出反应

内存使用率高:我认为一旦建立投影就应该一样

更长的启动时间:如果读取端缓存其预测并“记住”最近的更新事件,则不应重新应用整个事件序列。快照是一种缓解措施,但是如果您做很多快照,则可能是ES的错误选择。我认为,在微服务生态系统中,这个问题很小,可以在不中断服务的情况下屏蔽启动时间。实际上,在应用ES / CQRS时,您可以充分利用它,从而使微服务

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

事件存储中的序列化事件:如果具有非序列化事件表示形式很重要,则可以使用面向文档的数据库,但是如果这样做是为了对事件有效负载进行查询,则可以缺少ES / CQRS的要点。 ES意味着将所有数据处理从DB端移到应用程序层,其中每个部分都会快速更改,并且所有操作都是无状态的。这样可以增强可伸缩性和容错能力,并提供塑造团队组织的方法,例如让前端男孩/女孩轻松地用javascript编写他/她的BFF。

我希望将这一原则付诸实践,并从这种令人兴奋的方法中受益。