Event Sourcing Pattern 事件源模式
Use an append-only store to record the full series of events that describe actions taken on data in a domain, rather than storing just the current state, so that the store can be used to materialize the domain objects. This pattern can simplify tasks in complex domains by avoiding the requirement to synchronize the data model and the business domain; improve performance, scalability, and responsiveness; provide consistency for transactional data; and maintain full audit trails and history that may enable compensating actions.
使用追加存储来记录在域中所采取的数据的完整系列事件,而不是存储当前状态,因此该存储区可以用来实现域对象。这种模式可以简化复杂的领域中的任务,以避免要求同步的数据模型和业务领域,提高性能,可扩展性和响应性;提供一致性的事务数据,并保持完整的审计跟踪和历史,可能使补偿操作。
Context and Problem 问题和背景
Most applications work with data, and the typical approach is for the application to maintain the current state of the data by updating it as users work with the data. For example, in the traditional create, read, update, and delete (CRUD) model a typical data process will be to read data from the store, make some modifications to it, and update the current state of the data with the new values—often by using transactions that lock the data.
大多数应用程序使用数据,其应用程序的典型做法是通过更新它为用户提供数据的工作来保持数据的当前状态。例如,在传统的创建、读取、更新和删除(CRUD)模型的典型数据处理将是从存储与新的值时─读取数据,进行一些修改它,并更新数据的当前状态常常通过使用锁定数据的事务。
The CRUD approach has some limitations:
该CRUD方法有一定的局限性:
- The fact that CRUD systems perform update operations directly against a data store may hinder performance and responsiveness, and limit scalability, due to the processing overhead it requires.
- 该CRUD系统直接对数据存储器进行更新操作的事实可能会阻碍性能和响应,并限制可扩展性,由于处理开销它需要。
- In a collaborative domain with many concurrent users, data update conflicts are more likely to occur because the update operations take place on a single item of data.
- 与许多并发用户的协作域,数据更新冲突更可能发生,因为更新操作发生在数据的单个项目。
- Unless there is an additional auditing mechanism, which records the details of each operation in a separate log, history is lost.
- 除非有另外的审核机制,它记录每个操作的细节在一个单独的日志,历史将丢失。
Note:
For a deeper understanding of the limits of the CRUD approach see “CRUD, Only When You Can Afford It” on MSDN.
Solution 解决方案
The Event Sourcing pattern defines an approach to handling operations on data that is driven by a sequence of events, each of which is recorded in an append-only store. Application code sends a series of events that imperatively describe each action that has occurred on the data to the event store, where they are persisted. Each event represents a set of changes to the data (such as AddedItemToOrder).
事件源模式定义了一种以事件为驱动的数据处理操作的方法,其中每一个被记录在一个附加的存储。应用程序代码发送的一系列事件,势必描述每个事件数据存储操作,在那里被保持。每个事件表示一组数据的变化(如AddedItemToOrder)。
The events are persisted in an event store that acts as the source of truth or system of record (the authoritative data source for a given data element or piece of information) about the current state of the data. The event store typically publishes these events so that consumers can be notified and can handle them if needed. Consumers could, for example, initiate tasks that apply the operations in the events to other systems, or perform any other associated action that is required to complete the operation. Notice that the application code that generates the events is decoupled from the systems that subscribe to the events.
事件持久保存在一个事件存储,作为关于当前数据状态的数据源的真理或系统记录(给定数据元素或信息的权威数据源)。事件存储通常会发布这些事件,以便用户可以被通知,如果需要的话可以处理它们。例如,消费者可以启动将操作应用于其他系统的任务,或者执行完成该操作所需的任何其他相关操作的任务。通知,生成事件的应用程序代码是从订阅事件的系统中分离的。
Typical uses of the events published by the event store are to maintain materialized views of entities as actions in the application change them, and for integration with external systems. For example, a system may maintain a materialized view of all customer orders that is used to populate parts of the UI. As the application adds new orders, adds or removes items on the order, and adds shipping information, the events that describe these changes can be handled and used to update the materialized view.
事件存储发布的事件的典型用途是在应用程序更改时,维护实体视图,并与外部系统集成。例如,一个系统可以保持一个物化视图的所有客户订单,用于填充部分的用户界面。当应用程序添加新的订单,添加或删除订单上的项目,并增加航运信息,描述这些变化的事件可以处理和用于更新物化视图。
Note:
See the Materialized View pattern for more information.
In addition, at any point in time it is possible for applications to read the history of events, and use it to materialize the current state of an entity by effectively “playing back” and consuming all the events related to that entity. This may occur on demand in order to materialize a domain object when handling a request, or through a scheduled task so that the state of the entity can be stored as a materialized view to support the presentation layer.
此外,在任何时候,应用程序可能读取事件的历史,并使用它来实现当前状态的一个实体,有效地“打回”和消费的所有事件相关的实体。这可能会发生在要求,以实现域对象时,处理请求,或通过一个预定的任务,使该实体的状态可以被存储为一个物化视图,以支持表示层。
Figure 1 shows a logical overview of the pattern, including some of the options for using the event stream such as creating a materialized view, integrating events with external applications and systems, and replaying events to create projections of the current state of specific entities.
图1显示模式的一个逻辑的概述,包括一些使用事件流如创建物化视图的选择,事件与外部应用程序和系统的集成,和回放事件创建特定实体的当前状态预测。
Figure 1 - An overview and example of the Event Sourcing pattern
图1:事件源模式的概述和示例
The Event Sourcing pattern provides many advantages, including the following:
事件源模式提供了许多优点,其中包括以下内容:
- Events are immutable and so can be stored using an append-only operation. The user interface, workflow, or process that initiated the action that produced the events can continue, and the tasks that handle the events can run in the background. This, combined with the fact that there is no contention during the execution of transactions, can vastly improve performance and scalability for applications, especially for the presentation level or user interface.
- 事件是不可变的,因此可以使用仅追加操作被保存。用户界面,工作流或发起产生该事件可以继续,并且处理事件可以在后台运行的任务的操作过程。与事实有交易的执行过程中没有争用,可以极大地提高应用程序的性能和可扩展性,特别是对于呈现水平或用户接口相结合。
- Events are simple objects that describe some action that occurred, together with any associated data required to describe the action represented by the event. Events do not directly update a data store; they are simply recorded for handling at the appropriate time. These factors can simplify implementation and management.
- 事件是描述发生一些操作,连同以描述由事件所表示的反应所需要的任何相关联的数据的简单对象。事件不直接更新数据存储;它们被简单地记录在适当的时间处理。这些因素可以简化实施和管理。
- Events typically have meaning for a domain expert, whereas the complexity of the object-relational impedance mismatch might mean that a database table may not be clearly understood by the domain expert. Tables are artificial constructs that represent the current state of the system, not the events that occurred.
- 事件通常意为一个领域的专家,而对象关系阻抗不匹配的复杂性,可能意味着一个数据库表可能无法清晰的领域专家的理解。表是人工构建体表示的系统中,不发生的事件的当前状态。
- Event sourcing can help to prevent concurrent updates from causing conflicts because it avoids the requirement to directly update objects in the data store. However, the domain model must still be designed to protect itself from requests that might result in an inconsistent state.
- 事件源可以帮助防止造成冲突,因为它避免了直接更新数据存储对象的要求并发更新。但是,域模型仍然必须旨在保护自己免受可能导致不一致的状态请求。
- The append-only storage of events provides an audit trail that can be used to monitor actions taken against a data store, regenerate the current state as materialized views or projections by replaying the events at any time, and assist in testing and debugging the system. In addition, the requirement to use compensating events to cancel changes provides a history of changes that were reversed, which would not be the case if the model simply stored the current state. The list of events can also be used to analyze application performance and detect user behavior trends, or to obtain other useful business information.
- 事件的唯一附加的存储提供了可用于监测对数据存储所采取的行动,以在任何时间重播事件重新生成当前状态为物化视图或突出,并协助测试和调试的系统的审计跟踪。此外,该规定使用补偿事件取消改变提供颠倒了变化,这不会是如果模型简单地存储在当前状态的情况下的历史记录。事件列表也可用于分析应用程序的性能和检测用户行为的趋势,或者获得其它有用的商业信息。
- The decoupling of the events from any tasks that perform operations in response to each event raised by the event store provides flexibility and extensibility. For example, the tasks that handle events raised by the event store are aware only of the nature of the event and the data it contains. The way that the task is executed is decoupled from the operation that triggered the event. In addition, multiple tasks can handle each event. This may enable easy integration with other services and systems that need only listen for new events raised by the event store. However, the event sourcing events tend to be very low level, and it may be necessary to generate specific integration events instead.
- 事件从响应于由事件存储引发每个事件执行操作的任何任务的解耦提供了灵活性和可扩展性。例如,处理由事件存储引发的事件的任务都知道只的事件的性质和它包含的数据。时所执行的任务的方式是从触发事件的操作脱钩。此外,多个任务能处理每个事件。这可以使与其他服务和系统,只需要监听的事件存储提出了新的事件易于集成。但是,事件源事件往往是非常低的水平,并且可能有必要产生特定积分事件来代替。
Note:
Event sourcing is commonly combined with the CQRS pattern by performing the data management tasks in response to the events, and by materializing views from the stored events.
事件源通常与由响应于事件的执行数据管理任务,并且通过物化从所存储的事件视图的CQRS图案相结合。
Issues and Considerations 问题和注意事项
Consider the following points when deciding how to implement this pattern:
在决定如何实施这一模式时,请考虑以下几点:
- The system will only be eventually consistent when creating materialized views or generating projections of data by replaying events. There is some delay between an application adding events to the event store as the result of handling a request, the events being published, and consumers of the events handling them. During this period, new events that describe further changes to entities may have arrived at the event store. 创建物化视图或重放事件产生的数据预测时,系统只会是最终一致。有一个应用程序添加事件,事件店作为处理请求的结果之间有一些延迟,事件被公布,事件处理消费者他们。在此期间,该描述的实体进一步修改新的事件可能已在事件存储到了。
创建物化视图或重放事件产生的数据预测时,系统只会是最终一致。有一个应用程序添加事件,事件店作为处理请求的结果之间有一些延迟,事件被公布,事件处理消费者他们。在此期间,该描述的实体进一步修改新的事件可能已在事件存储到了。
-
Note:
See the Data Consistency Primer for information about eventual consistency.
- The event store is the immutable source of information, and so the event data should never be updated. The only way to update an entity in order to undo a change is to add a compensating event to the event store, much as you would use a negative transaction in accounting. If the format (rather than the data) of the persisted events needs to change, perhaps during a migration, it can be difficult to combine existing events in the store with the new version. It may be necessary to iterate through all the events making changes so that they are compliant with the new format, or add new events that use the new format. Consider using a version stamp on each version of the event schema in order to maintain both the old and the new event formats.
- 事件存储是信息的不可变源,因此事件数据不应该被更新。为了撤销变更更新实体的唯一方法是将赔偿事件添加到事件存储,就像你会在会计使用负数交易。如果持久事件的格式(而不是数据)需要改变,或者一个在迁移过程中,可能难以对现有的事件在商店用新版本合并。可能有必要通过进行变更的所有事件进行迭代,使它们符合新格式或添加使用该新格式的新事件。考虑使用上的事件模式的每个版本的版本戳,以保持双方的旧的和新的事件的格式。
- Multi-threaded applications and multiple instances of applications may be storing events in the event store. The consistency of events in the event store is vital, as is the order of events that affect a specific entity (the order in which changes to an entity occur affects its current state). Adding a timestamp to every event is one option that can help to avoid issues. Another common practice is to annotate each event that results from a request with an incremental identifier. If two actions attempt to add events for the same entity at the same time, the event store can reject an event that matches an existing entity identifier and event identifier.
- 多线程应用程序和应用程序的多个实例可以被存储在事件存储事件。在事件存储事件的一致性是重要的,为的是影响特定实体(的顺序改变为一个实体发生影响其当前状态)的事件的顺序。添加时间戳的每一个事件是一个选项,可以帮助避免的问题。另一种常见的做法是注释每一个从与增量标识符的请求的结果的事件。如果两个动作试图同时添加事件为同一实体,事件存储可以拒绝匹配现有的实体标识符和事件标识符的事件。
- There is no standard approach, or ready-built mechanisms such as SQL queries, for reading the events to obtain information. The only data that can be extracted is a stream of events using an event identifier as the criteria. The event ID typically maps to individual entities. The current state of an entity can be determined only by replaying all of the events that relate to it against the original state of that entity.
- 没有标准的做法,还是准备建机制,如SQL查询,阅读的事件来获取信息。可以提取的唯一数据是使用事件标识符作为条件事件流。事件ID通常映射到各个实体。一个实体的当前状态,只能通过重放所有涉及到它针对该实体的原始状态的事件来确定。
- The length of each event stream can have consequences on managing and updating the system. If the streams are large, consider creating snapshots at specific intervals such as a specified number of events. The current state of the entity can be obtained from the snapshot and by replaying any events that occurred after that point in time.
每个事件流的长度可对管理和更新系统的后果。如果流很大,可以考虑创建在特定的时间间隔的快照,如事件指定数量。该实体的当前状态可以从快照和重放,在时间点之后发生的任何事件而获得。
-
Note:
For more information about creating snapshots of data, see Snapshot on Martin Fowler’s Enterprise Application Architecture website and Master-Subordinate Snapshot Replication on MSDN.
- Even though event sourcing minimizes the chance of conflicting updates to the data, the application must still be able to deal with inconsistencies that may arise through eventual consistency and the lack of transactions. For example, an event that indicates a reduction in stock inventory might arrive in the data store while an order for that item is being placed, resulting in a requirement to reconcile the two operations; probably by advising the customer or creating a back order.
- 即使事件的采购,最大限度地减少冲突的数据更新的机会,应用程序仍然必须能够处理不一致,可能会出现通过最终的一致性和缺乏交易。例如,一个事件,表示库存库存减少可能到达的数据存储,而该项目的顺序是放置,从而使两者协调的要求,可能是通过通知客户或创建一个背面。
- Event publication may be “at least once,” and so consumers of the events must be idempotent. They must not reapply the update described in an event if the event is handled more than once. For example, if multiple instances of a consumer maintain an aggregate of a property of some entity, such as the total number of orders placed, only one must succeed in incrementing the aggregate when an “order placed” event occurs. While this is not an intrinsic characteristic of event sourcing, it is the usual implementation decision.
- 事件发布可能”至少一次,”,所以消费者的事件必须是幂等的。他们不能申请中描述一个事件如果事件处理是不止一次的更新。例如,如果消费者的多个实例保持总的一些实体的属性,如总订单的数量,只有一个必须成功递增骨料时,“订单”的事件发生。虽然这不是事件源的本质特征,它是通常的执行决策。
When to Use this Pattern 何时使用这种模式
This pattern is ideally suited to the following scenarios: 这种模式非常适合于以下情况:
- When you want to capture “intent,” “purpose,” or “reason” in the data. For example, changes to a customer entity may be captured as a series of specific event types such as Moved home, Closed account, or Deceased.
- 当你想捕捉“意图”,“目的”,或“原因”的数据。例如,对客户实体的更改可能被捕获为一系列特定的事件类型,如移动家庭、关闭帐户或死者。
- When it is vital to minimize or completely avoid the occurrence of conflicting updates to data.
- 当它是至关重要的,以尽量减少或完全避免发生冲突的更新数据。
- When you want to record events that occur, and be able to replay them to restore the state of a system; use them to roll back changes to a system; or simply as a history and audit log. For example, when a task involves multiple steps you may need to execute actions to revert updates and then replay some steps to bring the data back into a consistent state.
- 当你想记录发生的事件,并能够重放它们来恢复系统的状态;使用它们来回滚到系统的更改;或简单地作为历史和审计日志。例如,当一个任务涉及多个步骤,你可能需要执行动作,恢复更新,然后重播一些步骤,将数据带到一个一致的状态。
- When using events is a natural feature of the operation of the application, and requires little additional development or implementation effort. 当使用事件是应用程序的操作的一个自然特性,并且需要很少的额外开发或实施工作。
- When you need to decouple the process of inputting or updating data from the tasks required to apply these actions. This may be to improve UI performance, or to distribute events to other listeners such as other applications or services that must take some action when the events occur. An example would be integrating a payroll system with an expenses submission website so that events raised by the event store in response to data updates made in the expenses submission website are consumed by both the website and the payroll system.
- 当您需要将输入或更新数据从所需的任务中输入或更新的过程中,应用这些操作。这可能是为了提高用户界面的性能,或向其他诸如其他应用程序或服务的其他听众发布事件,这些事件必须在事件发生时采取一些行动。一个例子将整合一个工资系统的费用提交网站,使事件的事件存储在响应于数据更新的费用提交网站所提出的事件被消耗的网站和工资系统。
- When you want flexibility to be able to change the format of materialized models and entity data if requirements change, or—when used in conjunction with CQRS—you need to adapt a read model or the views that expose the data.
- 当你想要的灵活性,能够改变实体化模型和实体数据的格式,如果需求发生变化,或者,当结合使用CQRS,你需要适应一个读模式或泄露数据的看法。
- When used in conjunction with CQRS, and eventual consistency is acceptable while a read model is updated or, alternatively, the performance impact incurred in rehydrating entities and data from an event stream is acceptable.
- 当与CQRS结合,并最终一致性时使用的读模型被更新或是可以接受的,或者,在从事件流再水化的实体和数据产生的性能的影响是可以接受的。
This pattern might not be suitable in the following situations: 这种模式可能不适合在以下几种情况:
- Small or simple domains, systems that have little or no business logic, or non-domain systems that naturally work well with traditional CRUD data management mechanisms.
- 小或简单的领域,有很少或没有业务逻辑,或者非域系统的自然与传统的CRUD数据管理机制,良好的工作系统。
- Systems where consistency and real-time updates to the views of the data are required.
- 在需要的一致性和实时更新的数据的观点系统。
- Systems where audit trails, history, and capabilities to roll back and replay actions are not required.
- 系统中的审计跟踪,历史和能力来回滚和重放动作不是必需的。
- Systems where there is only a very low occurrence of conflicting updates to the underlying data. For example, systems that predominantly add data rather than updating it.
- 系统中仅存在冲突的更新的基础数据的一个非常低的发生。例如,主要是添加数据,而不是更新它的系统。
Example 例子
A conference management system needs to track the number of completed bookings for a conference so that it can check whether there are seats still available when a potential attendee tries to make a new booking. The system could store the total number of bookings for a conference in at least two ways:
会议管理系统需要跟踪完成预订会议,以便它可以检查是否有座位仍然可用,当一个潜在的与会者试图使一个新的预订数量。该系统可存储至少2种方式的会议的总数量的预订:
- The system could store the information about the total number of bookings as a separate entity in a database that holds booking information. As bookings are made or cancelled, the system could increment or decrement this number as appropriate. This approach is simple in theory, but can cause scalability issues if a large number of attendees are attempting to book seats during a short period of time. For example, in the last day or so prior to the booking period closing.
- 该系统可以存储在一个单独的实体在一个数据库中存储的预订信息的总数量的信息。由于预订被取消或取消,该系统可以增加或减少此号码为适当的。这种方法是简单的理论,但可引起可扩展性问题,如果有大量的与会者试图预订座位在很短的时间内。例如,在最后一天左右的预订期结束前。
- The system could store information about bookings and cancellations as events held in an event store. It could then calculate the number of seats available by replaying these events. This approach can be more scalable due to the immutability of events. The system only needs to be able to read data from the event store, or to append data to the event store. Event information about bookings and cancellations is never modified.
- 系统可以存储预订和取消举行事件存储事件信息。它可以通过重播这些事件的座位数计算。这种方法可以更具可扩展性由于事件的不变性。该系统只需要能够读取事件存储的数据,或将数据附加到事件存储。预订和取消的事件信息没有被修改过。
Figure 2 shows how the seat reservation sub-system of the conference management system might be implemented by using event sourcing.
图2显示了如何通过使用事件源来实现会议管理系统的座位保留子系统。
Figure 2 - Using event sourcing to capture information about seat reservations in a conference management system
图2 - 使用事件源捕获有关座位预订信息,在会议管理系统
The sequence of actions for reserving two seats is as follows:
保留2个席位的行动顺序如下:
- The user interface issues a command to reserve seats for two attendees. The command is handled by a separate command handler (a piece of logic that is decoupled from the user interface and is responsible for handling requests posted as commands).
- 用户界面问题的命令为2个与会者预订座位。该命令由一个单独的命令处理程序(一个从用户界面分离的逻辑程序)来处理,并负责处理请求的请求的处理。
- An aggregate containing information about all reservations for the conference is constructed by querying the events that describe bookings and cancellations. This aggregate is called SeatAvailability, and is contained within a domain model that exposes methods for querying and modifying the data in the aggregate.
通过查询预订和取消的事件描述构造集合包含的信息对所有预定的会议。可称为seatavailability这个集合包含在一个域模型暴露方法查询和修改的数据集合。
-
Note:
Some optimizations to consider are using snapshots (so that you don’t need to query and replay the full list of events to obtain the current state of the aggregate), and maintaining a cached copy of the aggregate in memory.
-
一些优化考虑使用快照(这样你就不需要查询和重播事件的全部列表以获得总的当前状态),并在内存中保持一个缓存副本。
- The command handler invokes a method exposed by the domain model to make the reservations.
- 命令处理程序调用域模型中暴露的方法,以使保留。
- The SeatAvailability aggregate records an event containing the number of seats that were reserved. The next time the aggregate applies events, all the reservations will be used to compute how many seats remain.
- 座位可利用情况汇总记录包含被保留的议席数目的事件。聚集应用于事件的下一个时间,所有的保留将被用来计算座椅多少保持。
- The system appends the new event to the list of events in the event store.
- The system appends the new event to the list of events in the event store.
If a user wishes to cancel a seat, the system follows a similar process except that the command handler issues a command that generates a seat cancellation event and appends it to the event store
如果用户希望取消的座椅,该系统遵循不同的是命令处理程序发出生成座椅取消事件,并将其追加到事件存储的指令类似的过程
As well as providing more scope for scalability, using an event store also provides a complete history, or audit trail, of the bookings and cancellations for a conference. The events recorded in the event store are the definitive and only source of truth. There is no need to persist aggregates in any other way because the system can easily replay the events and restore the state to any point in time.
以及为可扩展性提供了更多的范围,使用事件店内还提供了一个完整的历史,或审计追踪,预订和取消了会议。记录在事件存储的事件真相的权威性和唯一来源。没有必要继续存在聚集体以任何其他方式,因为该系统可以很容易地重放的事件和恢复状态的任何时间点。
Note:
You can find more information about this example in the chapter Introducing Event Sourcing in the patterns & practices guide CQRS Journey on MSDN.
Related Patterns and Guidance
The following patterns and guidance may also be relevant when implementing this pattern:
- Command and Query Responsibility Segregation (CQRS) Pattern. The write store that provides the immutable source of information for a CQRS implementation is often based on an implementation of the Event Sourcing pattern. The Command and Query Responsibility Segregation pattern describes how to segregate the operations that read data in an application from the operations that update data by using separate interfaces.
- Materialized View Pattern. The data store used in a system based on event sourcing is typically not well suited to efficient querying. Instead, a common approach is to generate pre-populated views of the data at regular intervals, or when the data changes. The Materialized View pattern shows how this can be achieved.
- Compensating Transaction Pattern. The existing data in an event sourcing store is not updated; instead new entries are added that transition the state of entities to the new values. To reverse a change, compensating entries are used because it is not possible to simply reverse the previous change. The Compensating Transaction pattern describes how to undo the work that was performed by a previous operation.
- Data Consistency Primer. When using event sourcing with a separate read store or materialized views, the read data will not be immediately consistent; instead it will be only eventually consistent. The Data Consistency Primer summarizes the issues surrounding maintaining consistency over distributed data.
- Data Partitioning Guidance. Data is often partitioned when using event sourcing in order to improve scalability, reduce contention, and optimize performance. The Data Partitioning Guidance describes how to divide data into discrete partitions, and the issues that can arise.
More Information
- The article see “CRUD, Only When You Can Afford It” on MSDN.
- Introducing Event Sourcing and A CQRS and ES Deep Dive in the patterns & practices guide CQRS Journey on MSDN.
- The post Event Sourcing on Martin Fowler's blog.
- Greg Young’s post Why use Event Sourcing? On the Code Better website.