虽然我还没有实现分布式的事件源子系统(所以我不是专家),但我一直在研究和评估这种方法。事件溯源提供了许多关键优势:
- 可靠性
- 可扩展性
- 可进化性
- 审核
我相信还有更多。在很大程度上,事件溯源的好处取决于您将其与之进行比较的基线(CRUD、事件驱动的 DDD、CQRS 或其他)和领域。
让我们依次看一下:
可靠性
对于在系统更新时触发事件的事件驱动系统,您经常会遇到一个问题:如何同时更新系统状态并同时触发事件?如果第二次操作失败,则您的系统处于损坏、不一致的状态。事件溯源为此提供了一个简洁的解决方案,因为系统只需要一个操作来改变状态,这将自动地成功或失败:事件的写入。其他解决方案往往更复杂且可扩展性更低 - 2 阶段提交等。
这在大型、高事务系统中是一个很大的好处,其中组件在事务进行时一直在发生故障、更新或更换。随时终止进程而无需担心数据损坏或一致性的能力是一个很大的好处,可以帮助您在晚上睡觉。
在许多域中,您不会对相同的实体进行并发写入,或者您不需要事件,因为状态更改不会产生连锁反应,在这种情况下,事件溯源不太可能是一个好方法,并且像 CRUD 这样更简单的方法可能没问题。
可扩展性
首先,事件流使一致的写入非常高效 - 它只是一个仅附加的日志,这使得复制和“比较和设置”易于优化。在您需要保护不变量的情况下,像 Cassandra 这样的东西非常慢 - 也就是说,您需要针对“行”的当前状态验证命令,如果行发生更改,则在您有机会之前拒绝更新更新它。您要么需要使用“轻量级事务”来确保一致性,要么每个分区有一个写入器线程,这样您就可以确保在允许更新之前可以根据系统的当前状态成功验证命令。当然,您可以使用其中任何一种方法(单线程/轻量级事务)在 Cassandra 中实现事件存储。
不过,读取可扩展性是最大的性能优势 - 因为您可以通过从事件流中读取数据来构建任意数量的不同最终一致的投影(视图),并根据需要在这些视图上水平扩展查询服务.这些视图可以根据需要使用自定义数据库(Cassandra、图形数据库),以允许尽可能多地优化查询。它们可以存储非规范化数据,以允许在单个(非连接)数据库查询中获取所有必需的数据。他们甚至可以将投影状态存储在内存中,以获得最佳性能。虽然这可以在没有事件溯源的情况下实现,但实现起来要复杂得多。
如果您没有复杂的查询和高可扩展性要求,事件溯源可能不是正确的解决方案。
可进化性
如果您需要以新的方式查看数据,例如在应用中创建新的客户端应用或屏幕,则可以很容易地将事件流的新投影添加为新的独立服务。如果您需要将一些数据添加到您错过的现有读取视图中,或者修复读取视图中的错误,您可以使用事件流重建视图并丢弃旧视图。与非事件源案例相比,这里的优势是:
- 您无需编写数据库迁移代码,然后编写代码以在事件进入时使视图保持最新。相反,您只需编写代码以使其保持最新,然后在来自的事件上运行它时间的开始。
- 与此相关,您可以进行更新,而无需关闭查询服务来进行架构更改 - 相反,只需让旧服务版本针对旧数据库运行,使用新服务版本生成一个新数据库,当它赶上事件流时,只需自动切换然后清理旧服务和数据库,一旦你满意新服务和数据库稳定(请注意,旧服务将同时保持最新状态,如果你需要回滚!)。如果没有事件溯源,这可能很难实现。
- 如果您需要将任何时间信息添加到您的视图中(例如,上次更新的时间、创建时间),这些信息已经可用并且易于添加,但如果没有事件溯源,则无法追溯添加。
请注意,以上内容不是关于修改事件流(这很棘手,请参阅下面我对挑战的评论) - 它是关于使用现有事件流来增强视图或创建新视图。
有一些简单的方法可以在不使用事件溯源的情况下做到这一点,例如使用数据库视图(使用 RDBMS),但它们的可扩展性不高。
事件溯源在可演化性方面也存在一些挑战 - 您需要处理事件版本控制,可能会结合使用弱事件模式(因此您可以添加具有默认值的属性)和流替换(当您想要做更大的事情时)更改为您的活动)。 Greg Young 正在写一本关于这方面的好书。
审核
正如你所说,你对此不感兴趣。