Event-Sourcing模式

Event-Sourcing模式使用仅附加存储来记录或描述域中数据所采取的动作,从而记录完整的一系列系列事件,而不是仅存储实体的当前状态。因为存储包含全部的事件,可以用来具体化域对象。Event-Sourcing模式可以简化复杂的域中的任务,避免了数据模型和业务领域的同步和引发的争用问题;增强性能,扩展性,以及响应;为事物数据提供一致性;保留全部的事件执行历史,可以跟踪和实现回滚之类的补偿操作。

问题

大多数的应用都会涉及到数据的处理,而通常的方法是由应用来保证数据的状态,当用户请求数据的时候,就会更新数据的状态。举个例子,在传统的CRUD模型中,典型的数据处理过程是,从数据仓库读取数据,做出一些修改,使用新的数据更新(通常通过事务来保护数据)当前数据的状态。CRUD的方式会有如下的一些限制:

想要更深入的了解CRUD架构的一些限制,可以参考微软的CRUD, Only When You Can Afford It一文。

解决方案

Event-Sourcing模式定义了一种处理由一系列事件来驱动的数据操作的方法,每一事件都记录在一个仅能追加的存储中。应用将生成一系列事件来描述每一个动作,每个事件对应存储数据发生的变更,然后将这些事件持久化。每个事件都表示一组数据的变化(比如在订单中增加数据项)。

事件可以持久化到事件存储仓库中。事件存储仓库作为对当前数据的状态的数据源或记录系统(数据源或信息的权威数据源)。事件存储通常会发布这些事件,以便可以通知消费者,并在消费者有需要的时候处理它们。例如,消费者可以初始化一些任务将事件中的操作应用于其他系统来执行,或执行完成该操作所需的任何其他相关操作。注意,生成事件的应用程序代码与订阅事件的系统是解耦的。

通常来说,事件存储所发布的事件是用来在应用程序对实体进行修改的时候,更新实体的Materialized视图,并与外部系统集成。例如,系统可以维护所有客户订单的Materialized视图,这些订单信息用于填充UI的部分。当应用程序添加新的订单,添加或删除项目的顺序,并添加航运信息,消费者将处理描述这些变化的事件,并更Materialized视图。

参考Materialized-View模式来了解更多的详细信息。

此外,事件存储可以让应用在任何时候读取事件的历史,并通过对事件历史进行有效地“回放”和消耗有关该实体的所有事件来计算实体的当前状态。很多时候,可能会因为客户端请求,或通过一个预定的定时任务,来重新计算实体的当前状态。其中,定时任务可以将计算的状态生成Materialized视图,并存储起来来提高查询效率。

图1展示了Event-Sourcing模式逻辑的概览图。其中也包括了使用事件流来创建一个Materialized视图,并与其他外部应用和系统集成,以及通过重复事件流来展示某些实体的当前状态。

图1. 关于Event-Sourcing模式的例子和概览

Event-Sourcing模式有很多的优点,包括:

Event-Sourcing通常都是配合CQRS模式来执行数据管理任务的。

实现Event-Sourcing模式的问题和顾虑

当在考虑实现Event-Sourcing模式的时候,需要考虑如下一些问题:

想了解针对数据建立快照方面更多的信息,可以参考Martin Fowler的Enterprise Application Architecture website以及Master-Subordinate Snapshot Replication on MSDN.

何时使用Event-Sourcing模式

Event-Sourcing模式很适合以下场景:

Event-Sourcing模式在如下的情况下不太适用:

使用举例

某个会议管理系统需要跟踪完成预订的会议,该系统可以在某个希望参加会议的人尝试申请参加的时候,检查是否还有空闲座位。该系统可以以至少两种方式来存储预定库存的总数:

图2显示了如何使用Event-Sourcing来实现会议管理系统的座席预订子系统。

图2在会议管理系统中使用Event-Sourcing获取座位预订信息

保留两个座位的动作顺序如下:

  1. 用户界面发出一个命令,为两位与会者预留座位。该命令由一个单独的命令处理程序处理(一个与用户界面解耦的应用逻辑,负责处理作为命令发送的请求)。
  2. 通过请求所有的预定和取消会议的事件,来获取一个包含全部预定信息的数据集合。这个数据集合叫SeatAvailability,并支持查询其中的库存信息。

    可以使用快照来做一些优化(因此开发者不需要查询和重放事件的完整列表以获取的SeatAvailability的当前状态),并在内存中维护聚合的缓存副本。

  3. CommandHandler调用由域模型的方法来预定或取消。
  4. SeatAvailability记录包含座位库存。下一次计算库存数时,将遍历所有的预订时间来计算还有多少库存。
  5. 该系统增加的新事件会存储在事件仓库中。

如果用户希望取消座位,该系统遵循类似的过程,由命令处理程序产生一个取消座位的事件并将其添加到事件存储中。除了提供了更好的扩展性外,事件仓库为所有的预定和取消操作都保留了操作历史,无论是为了跟踪,或者分析挖掘,都提供了良好的支持。因为事件仓库中记录的事件是真实的唯一来源。系统没有必要持久化其他的状态信息,因为可以很容易地重新遍历事件并计算任何时间点实体状态。

开发者可以在Introducing Event Sourcing中更详细的了解这个例子。

相关的其他模式

当考虑实现Event-Sourcing模式的时候,也可以参考如下相关模式: