이벤트 소싱 패턴Event Sourcing pattern

데이터의 현재 상태만 도메인에 저장하는 대신 추가 전용 저장소를 사용하여 해당 데이터에 수행된 전체 작업을 기록합니다.Instead of storing just the current state of the data in a domain, use an append-only store to record the full series of actions taken on that data. 저장소는 레코드 시스템 역할을 하며, 도메인 개체를 구체화하는 데 사용될 수 있습니다.The store acts as the system of record and can be used to materialize the domain objects. 이렇게 하면 데이터 모델과 비즈니스 도메인을 동기화할 필요가 없고 성능, 확장성 및 응답성이 향상되므로 복잡한 도메인의 태스크를 간소화할 수 있습니다.This can simplify tasks in complex domains, by avoiding the need to synchronize the data model and the business domain, while improving performance, scalability, and responsiveness. 또한 트랜잭션 데이터에 일관성을 제공하고 보정 작업에 사용할 수 있는 전체 감사 추적 및 기록을 유지할 수 있습니다.It can also provide consistency for transactional data, and maintain full audit trails and history that can 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 it. 예를 들어 기존의 CRUD(만들기, 읽기, 업데이트 및 삭제) 모델에서 일반적인 데이터 프로세스는 저장소에서 데이터를 읽고 수정한 다음 주로 데이터를 잠그는 트랜잭션을 사용하여 데이터의 현재 상태를 새 값으로 업데이트하는 것입니다.For example, in the traditional create, read, update, and delete (CRUD) model a typical data process is 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 시스템은 데이터 저장소에서 직접 업데이트 작업을 수행하므로 성능 및 응답 속도가 저하되고 필요한 처리 오버헤드로 인해 확장성이 제한될 수 있습니다.CRUD systems perform update operations directly against a data store, which can slow down performance and responsiveness, and limit scalability, due to the processing overhead it requires.

  • 많은 동시 사용자가 있는 공동 작업 도메인에서 데이터 업데이트 충돌은 대체로 업데이트 작업이 단일 데이터 항목에서 수행되기 때문에 발생합니다.In a collaborative domain with many concurrent users, data update conflicts are more likely because the update operations take place on a single item of data.

  • 각 작업의 세부 정보를 개별 로그에 기록하는 추가 감사 메커니즘이 없으면 기록이 유실됩니다.Unless there's an additional auditing mechanism that records the details of each operation in a separate log, history is lost.

CRUD 접근 방식의 제한에 대한 자세한 내용은 CRUD, Only When You Can Afford It(CRUD, 사용 가능한 경우에만)을 참조하세요.For a deeper understanding of the limits of the CRUD approach see CRUD, Only When You Can Afford It.

해결 방법Solution

이벤트 소싱 패턴은 각각 추가 전용 저장소에 기록되는 일련의 이벤트에 의해 구동되는 데이터 작업 처리 방법을 정의합니다.The Event Sourcing pattern defines an approach to handling operations on data that's 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're persisted. 각 이벤트는 데이터에 대한 변경 집합(예: AddedItemToOrder)을 나타냅니다.Each event represents a set of changes to the data (such as AddedItemToOrder).

이벤트는 데이터의 현재 상태에 대한 레코드 시스템(권한 있는 데이터 원본) 역할을 하는 이벤트 저장소에 저장됩니다.The events are persisted in an event store that acts as the system of record (the authoritative data source) 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's 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. 예를 들어 시스템에서 UI 부분을 채우는 데 사용되는 모든 고객 주문의 구체화된 뷰를 유지 관리할 수 있습니다.For example, a system can maintain a materialized view of all customer orders that's 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.

또한 언제든지 애플리케이션이 이벤트 기록을 읽고 해당 엔터티와 관련된 모든 이벤트를 재생 및 이용하여 엔터티의 현재 상태를 구체화하는 데 사용할 수 있습니다.In addition, at any point it's possible for applications to read the history of events, and use it to materialize the current state of an entity by playing back and consuming all the events related to that entity. 이 작업은 프레젠테이션 레이어를 지원하기 위해 엔터티 상태를 구체화된 뷰로 저장할 수 있도록 예약된 태스크를 통해 또는 요청을 처리할 때 도메인 개체 구체화 요청 시 수행될 수 있습니다.This can occur on demand 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.

이 그림은 구체화된 뷰 만들기, 외부 애플리케이션 및 시스템과 이벤트 통합, 이벤트를 재생하여 특정 엔터티의 현재 상태 프로젝션 만들기 등의 몇 가지 이벤트 스트림 사용 옵션을 포함하여 패턴 개요를 보여 줍니다.The figure shows an 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.

이벤트 소싱 패턴의 개요 및 예

이벤트 소싱 패턴에는 다음과 같은 장점이 있습니다.The Event Sourcing pattern provides the following advantages:

  • 이벤트를 변경할 수 없으며 추가 전용 작업을 사용하여 저장할 수 있습니다.Events are immutable and can be stored using an append-only operation. 이벤트를 시작한 사용자 인터페이스, 워크플로 또는 프로세스가 계속 진행될 수 있고, 이벤트를 처리하는 태스크를 백그라운드에서 실행할 수 있습니다.The user interface, workflow, or process that initiated an event can continue, and tasks that handle the events can run in the background. 트랜잭션 처리 중 경합이 없다는 사실과 더불어 이 장점으로 인해 특히 프레젠테이션 수준 또는 사용자 인터페이스에서 애플리케이션의 성능과 확장성이 훨씬 향상됩니다.This, combined with the fact that there's no contention during the processing 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 don't directly update a data store. 단순히 적절한 시간에 처리하기 위해 기록됩니다.They're simply recorded for handling at the appropriate time. 이렇게 하면 구현 및 관리를 간소화할 수 있습니다.This can simplify implementation and management.

  • 이벤트는 일반적으로 도메인 전문가에게 의미가 있는 반면, 개체 관계형 임피던스 불일치로 인해 복잡한 데이터베이스 테이블을 이해하기 어려울 수 있습니다.Events typically have meaning for a domain expert, whereas object-relational impedance mismatch can make complex database tables hard to understand. 테이블은 발생한 이벤트가 아니라 시스템의 현재 상태를 나타내는 인공 구문입니다.Tables are artificial constructs that represent the current state of the system, not the events that occurred.

  • 이벤트 소싱을 사용하면 데이터 저장소의 개체를 직접 업데이트하지 않아도 되기 때문에 동시 업데이트로 인한 충돌을 방지할 수 있습니다.Event sourcing can help 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 wouldn't 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 event store raises events, and tasks perform operations in response to those events. 이렇게 태스크와 이벤트를 분리하면 유연성과 확장성이 제공됩니다.This decoupling of the tasks from the events provides flexibility and extensibility. 태스크는 이벤트 유형과 이벤트 데이터를 알고 있지만 이벤트를 트리거한 작업에 대해서는 알 수 없습니다.Tasks know about the type of event and the event data, but not about the operation that triggered the event. 또한 여러 태스크가 각 이벤트를 처리할 수 있습니다.In addition, multiple tasks can handle each event. 이 경우 이벤트 저장소에서 발생하는 새 이벤트만 수신 대기하는 다른 서비스 및 시스템과 쉽게 통합할 수 있습니다.This enables easy integration with other services and systems that only listen for new events raised by the event store. 그러나 이벤트 소싱 이벤트는 대체로 매우 낮은 수준이며, 대신 특정 통합 이벤트를 생성해야 할 수도 있습니다.However, the event sourcing events tend to be very low level, and it might be necessary to generate specific integration events instead.

이벤트 소싱은 일반적으로 이벤트에 대한 응답으로 데이터 관리 태스크를 수행하고 저장된 이벤트에서 뷰를 구체화하여 CQRS 패턴과 결합됩니다.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.

문제 및 고려 사항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's 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 might have arrived at the event store.

참고

결과적 일관성에 대한 자세한 내용은 데이터 일관성 입문서를 참조하세요.See the Data Consistency Primer for information about eventual consistency.

이벤트 저장소는 영구적인 정보 소스이므로 이벤트 데이터가 업데이트되면 안 됩니다.The event store is the permanent source of information, and so the event data should never be updated. 변경 내용을 취소하기 위해 엔터티를 업데이트하는 유일한 방법은 이벤트 저장소에 보정 이벤트를 추가하는 것입니다.The only way to update an entity to undo a change is to add a compensating event to the event store. 지속형 이벤트의 형식(데이터 아님)을 변경해야 하는 경우, 마이그레이션 중에 저장소의 기존 이벤트를 새 버전과 결합하기 어려울 수 있습니다.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 might be necessary to iterate through all the events making changes so they're 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 to maintain both the old and the new event formats.

다중 스레드 애플리케이션과 여러 애플리케이션 인스턴스가 이벤트 저장소에 이벤트를 저장할 수 있습니다.Multi-threaded applications and multiple instances of applications might 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 that changes occur to an entity affects its current state). 모든 이벤트에 타임스탬프를 추가하면 문제를 방지할 수 있습니다.Adding a timestamp to every event can help to avoid issues. 또 다른 일반적인 방법은 요청에서 발생하는 각 이벤트에 증분 식별자를 주석으로 추가하는 것입니다.Another common practice is to annotate each event resulting 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.

정보를 얻기 위해 이벤트를 읽는 표준 방법이나 SQL 쿼리와 같은 기존 메커니즘은 없습니다.There's no standard approach, or existing 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. 이벤트 ID는 일반적으로 개별 엔터티에 매핑됩니다.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.

각 이벤트 스트림의 길이는 시스템 관리 및 업데이트에 영향을 줍니다.The length of each event stream affects 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. 데이터 스냅숏을 만드는 방법에 대한 자세한 내용은 Martin Fowler 엔터프라이즈 애플리케이션 아키텍처 웹 사이트의 스냅숏마스터-하위 스냅숏 복제를 참조하세요.For more information about creating snapshots of data, see Snapshot on Martin Fowler’s Enterprise Application Architecture website and Master-Subordinate Snapshot Replication.

이벤트 소싱이 데이터 업데이트의 충돌 가능성을 최소화하더라도 애플리케이션이 결과적 일관성과 트랜잭션 부족에서 발생하는 불일치를 처리할 수 있어야 합니다.Even though event sourcing minimizes the chance of conflicting updates to the data, the application must still be able to deal with inconsistencies that result from 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 either by advising the customer or creating a back order.

이벤트 게시는 "최소한 한 번"일 수 있으므로 이벤트 소비자가 멱등적이어야 합니다.Event publication might 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 an entity's property, such as the total number of orders placed, only one must succeed in incrementing the aggregate when an order placed event occurs. 이벤트 소싱의 주요 특성은 아니지만 일반적인 구현 결정입니다.While this isn't a key characteristic of event sourcing, it's the usual implementation decision.

이 패턴을 사용해야 하는 경우When to use this pattern

다음 시나리오에서 이 패턴을 사용하세요.Use this pattern in the following scenarios:

  • 데이터에 의도, 목적 또는 이유를 캡처하려는 경우.When you want to capture intent, purpose, or reason in the data. 예를 들어 고객 엔터티의 변경 내용을 이사, 계정 폐쇄 또는 _사망_과 같은 일련의 특정 이벤트 유형으로 캡처할 수 있습니다.For example, changes to a customer entity can be captured as a series of specific event types such as Moved home, Closed account, or Deceased.

  • 데이터 업데이트 충돌 발생을 최소화하거나 완전히 방지하는 것이 중요한 경우.When it's 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, roll back changes, or keep a history and audit log. 예를 들어 태스크에 여러 단계가 필요한 경우 업데이트를 되돌리는 작업을 실행한 후 일부 단계를 재생하여 데이터를 일관된 상태로 되돌려야 할 수도 있습니다.For example, when a task involves multiple steps you might 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. UI 성능을 향상하거나 이벤트 발생 시 작업을 수행하는 다른 수신기에 이벤트를 분배하기 위한 것일 수 있습니다.This might be to improve UI performance, or to distribute events to other listeners that take action when the events occur. 예를 들어 웹 사이트의 데이터 업데이트에 대한 응답으로 이벤트 저장소에서 발생하는 이벤트가 웹 사이트와 급여 시스템 둘 다에서 이용되도록 급여 시스템과 비용 제출 웹 사이트를 통합합니다.For example, integrating a payroll system with an expense submission website so that events raised by the event store in response to data updates made in the website are consumed by both the website and the payroll system.

  • 요구 사항 변경에 따라 구체화된 모델 및 엔터티 데이터의 형식을 변경할 수 있는 유연성을 원하거나 CQRS와 함께 사용 시 읽기 모델 또는 데이터를 표시하는 뷰를 조정해야 하는 경우.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 the performance impact of rehydrating entities and data from an event stream is acceptable.

이 패턴은 다음과 같은 경우에 유용하지 않을 수 있습니다.This pattern might not be useful in the following situations:

  • 작거나 단순한 도메인, 비즈니스 논리가 거의 없거나 전혀 없는 시스템 또는 기존의 CRUD 데이터 관리 메커니즘에서 기본적으로 잘 작동하는 비도메인 시스템.Small or simple domains, systems that have little or no business logic, or nondomain systems that naturally work well with traditional CRUD data management mechanisms.

  • 데이터 뷰에 대한 일관성 및 실시간 업데이트가 필요한 시스템.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's 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 booking. 다음과 같이 총 회의 예약 수를 둘 이상의 방법으로 저장할 수 있습니다.The system could store the total number of bookings for a conference in at least two ways:

  • 예약 정보를 보유하는 데이터베이스에 총 예약 수에 대한 정보를 별도의 엔터티로 저장할 수 있습니다.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 canceled, 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 append data to the event store. 예약 및 취소에 대한 이벤트 정보는 수정되지 않습니다.Event information about bookings and cancellations is never modified.

다음 다이어그램은 이벤트 소싱을 사용하여 회의 관리 시스템의 좌석 예약 하위 시스템을 구현할 수 있는 방법을 보여 줍니다.The following diagram illustrates how the seat reservation subsystem of the conference management system might be implemented using event sourcing.

이벤트 소싱을 사용하여 회의 관리 시스템에서 좌석 예약 정보 캡처

두 개의 좌석을 예약하기 위한 작업 시퀀스는 다음과 같습니다.The sequence of actions for reserving two seats is as follows:

  1. 사용자 인터페이스가 두 명의 참석자를 위한 좌석을 예약하는 명령을 실행합니다.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. 이 집계는 SeatAvailability라고 하며, 집계의 데이터를 쿼리하고 수정하기 위한 메서드를 노출하는 도메인 모델에 포함됩니다.This aggregate is called SeatAvailability, and is contained within a domain model that exposes methods for querying and modifying the data in the aggregate.

    고려할 몇 가지 최적화는 스냅숏 사용(집계의 현재 상태를 얻기 위해 전체 이벤트 목록을 쿼리 및 재생할 필요가 없음) 및 메모리에 캐시된 집계 사본 유지 관리입니다.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.

  3. 명령 처리기는 도메인 모델에서 노출된 메서드를 호출하여 예약을 수행합니다.The command handler invokes a method exposed by the domain model to make the reservations.

  4. SeatAvailability 집계는 예약된 좌석 수를 포함하는 이벤트를 기록합니다.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.

  5. 시스템이 이벤트 저장소의 이벤트 목록에 새 이벤트를 추가합니다.The system appends the new event to the list of events in the event store.

사용자가 좌석을 취소하는 경우 명령 처리기가 좌석 취소 이벤트를 생성하고 이벤트 저장소에 추가하는 명령을 사용한다는 점을 제외하고 시스템이 유사한 프로세스를 따릅니다.If a user cancels a seat, the system follows a similar process except 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 in the event store are the accurate record. 시스템이 쉽게 이벤트를 재생하고 상태를 특정 시점으로 복원할 수 있으므로 다른 방법으로 집계를 저장할 필요가 없습니다.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.

이 예제에 대한 정보는 이벤트 소싱 소개에서 확인할 수 있습니다.You can find more information about this example in Introducing Event Sourcing.

이 패턴을 구현할 때 다음 패턴 및 지침도 관련이 있을 수 있습니다.The following patterns and guidance might also be relevant when implementing this pattern:

  • CQRS(명령 및 쿼리 책임 분리).Command and Query Responsibility Segregation (CQRS) pattern. CQRS 구현에 대한 영구적 정보 소스를 제공하는 쓰기 저장소는 종종 이벤트 소싱 패턴 구현을 기반으로 합니다.The write store that provides the permanent source of information for a CQRS implementation is often based on an implementation of the Event Sourcing 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 prepopulated views of the data at regular intervals, or when the data changes. 이 작업을 수행하는 방법을 보여 줍니다.Shows how this can be done.

  • 보상 트랜잭션 패턴.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 isn't possible to simply reverse the previous change. 이전 작업에서 수행한 작업을 실행 취소하는 방법을 설명합니다.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 won't be immediately consistent, instead it'll be only eventually consistent. 분산 데이터에 대한 일관성 유지와 관련된 문제를 요약합니다.Summarizes the issues surrounding maintaining consistency over distributed data.

  • 데이터 분할 지침.Data Partitioning Guidance. 이벤트 소싱을 사용하여 확장성을 향상하고, 경합을 줄이고, 성능을 최적화하는 경우 데이터가 분할되는 경우가 많습니다.Data is often partitioned when using event sourcing to improve scalability, reduce contention, and optimize performance. 데이터를 개별 파티션으로 나누는 방법과 발생할 수 있는 문제에 대해 설명합니다.Describes how to divide data into discrete partitions, and the issues that can arise.