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.