超过7年的分布式企业级生产环境使用经验:16条保证事件溯源成功的准则 - continuousimprover


几周前,我结束了一场技术辩论,讨论如何进一步利用现有的事件溯源应用程序,以充分利用其旨在为您带来的好处。我已经写了许多帖子是关于陷阱、最佳实践以及如何在.NET中具体实现这一点。但是我仍然认为为您提供一些最重要的指导方针和启发式方法列表可能是有用的,我认为这些列表和启发式方法对于成功使用事件溯源是必不可少的。

除非您拥有一个复杂的域,该域必须处理在同一实体上一起工作的许多用户,否则请不要使用事件源。您不需要仅用于构建审核日志的事件源。即使您确实使用事件源,也可能不需要所有内容。通常,主数据也可以使用版本化架构进行处理。但是话又说回来,对于较小的团队,只有一个范式可能会更好。但是不要忽略NoSQL作为事件源的替代方法。

不要围绕不变性建模,而是围绕业务概念或数据模型对聚合建模。但是要小心,选择系统必须保护的不变性,以及可以使用更函数化的方法(或跨聚合的事务)来解决哪些不变性。如果不这样做,最终将产生巨大的聚合。(banq注:跨聚合事务是一种最终一致性事务)

不要对跨聚合事务教条主义,仅当您确实需要性能和并发性时,才强制每次事务仅涉及一个聚合。否则请保持务实。如果没有事务逻辑来保护跨聚合实现业务规则的复杂性,这可能是非常具有挑战性的,通常不值得。

请为每个聚合分配一个(自然的)分区键,这样,如果出于性能或存储方面的考虑,如果您需要你能用它来拆分事件存储。

一定要将事件存储视为业务中发生的重大审核日志,并且永远不要更改或重新排序事件。确保您可以明确指向事件存储中的某个位置,并查看在该检查点编号之后发生了哪些事件。这对于具有增量数据迁移的分布式事件存储和蓝/绿部署可能非常有用。但是,多年来我们一直在使用自己的Event Sourcing,因此能够从事件存储中诊断生产问题对我们至关重要。

不要把事件当作捕捉属性变化的一种方式。当开发人员将事件溯源作为技术解决方案而没有充分了解业务领域和/或不邀请业务人员参与时,通常会看到这种所谓的属性来源。考虑使用事件风暴事件建模作为映射业务流程的技术,而不是专注于数据模型。

不要将域事件用作域或服务之间的通信机制。事件源是特定域(或DDD术语中的有界上下文)的实现。使用更多的粗粒度事件进行域间通信,例如,通过将域事件投影到推送到消息总线或API网关的更高级别的事件中。

不要为了加快投影速度而改变事件CQRS的全部要点是您将命令端(域)和查询端(预测)之间的利益冲突分开。当业务域更改时,聚合和事件也会更改。当显示/查询需要更改时,投影也会更改。

根据功能需求明确说明要在事件中保留哪些信息。因此,如果您需要用户签署文档的全名,请使其成为事件的一部分。如果您只需要最新的全名,请计划相关事件。

请为事件提供唯一的标识符,并使用该标识符将各个投影与用于更新的最新事件相关联。非常适合用于诊断。

默认情况下,请使用自治的异步投影。是的,它可能会增加UI的复杂性,以处理投影的最终一致性,但会打开许多您需要针对性能进行优化的选项。使用适合目的的任何存储机制,在需要时进行重建以及在多台服务器上扩展投影仪以达到一流的性能。如果您确实需要作为域更改的一部分来更新特定的投影仪,请考虑等待投影仪赶上该域发布的最后一个事件。

请注意,在负载下,基于全局检查点对基于SQL的事件存储进行排队可能会导致您由于正在进行的事务而错过事件。使用back-off窗口或序列化对每个分区的事件存储进行写操作来解决此问题。
通过让投影机将自己标记为正在重建并将其进度显示为ETA等,将重建投影视为特殊状态。智能投影仪还会检测事件存储是否已回滚到较早的状态(由于数据库还原或生产补丁程序),并将自动重建自身。

尽一切努力使重建迅速。使用积极的缓存,使用NoSQL数据库,在内存中并分批进行项目,然后写入数据库,如果对投影有意义,则通过Steam进行项目流,分批从事件存储中读取(并且对异常处理要精明) ,使用ORM并从其工作单元中受益,以减少SQL语句。您甚至可以停止与该投影机不再相关的聚合的投影事件,例如,通过将特定聚合(流)的所有事件都可以归档的元数据添加到事件存储中。

不要合并来自多个投影的数据,除非您知道考虑到每个投影机的真实性和异步性,这是安全的。如果确实需要从其他聚合中捕获信息,请拥有投影的投影仪根据这些聚合的事件维护自己的私有查找。另一个例外示例是您从事件中构建数据仓库的情况,但是即使如此,您仍需要考虑一致性的权衡。

作为事件的测试单位,对整个投影机进行测试,并附带事件,并以查询结果或HTTP API作为输出。将投影仪本身及其存储投影的方式视为可随时间变化的实现细节。在此处查看如何以功能和可读的方式构建此类测试的示例。