{"componentChunkName":"component---src-templates-blog-post-js","path":"/springboot/Event-Sourcing과-CQRS-패턴-이벤트-기반-아키텍처-설계-실전-가이드/","result":{"data":{"site":{"siteMetadata":{"title":"Bottlehs Tech Blog","description":"웹·앱·AI 개발자 전병훈(bottlehs)의 기술 블로그. AI·LLM, JavaScript·Vue, Spring Boot, 알고리즘, 블록체인 실무 가이드를 다룹니다","siteUrl":"https://www.bottlehs.dev","author":{"name":"Jeon Byung Hun","summary":"개발을 즐기는 bottlehs - Engineer, MS, AI, FE, BE, OS, IOT, Blockchain, 설계, 테스트"},"social":{"github":"bottlehs","codepen":"bottlehs"}}},"markdownRemark":{"id":"4a5c7a21-cf79-5a3f-9a45-dd6971d443d1","excerpt":"Event Sourcing과 CQRS 패턴 - 이벤트 기반 아키텍처 설계 실전 가이드 Event Sourcing은 애플리케이션의 상태 변경을 일련의 이벤트로 저장하는 패턴이다. CQRS(Command Query Responsibility Segregation…","html":"<h1 id=\"event-sourcing과-cqrs-패턴---이벤트-기반-아키텍처-설계-실전-가이드\" style=\"position:relative;\"><a href=\"#event-sourcing%EA%B3%BC-cqrs-%ED%8C%A8%ED%84%B4---%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C\" aria-label=\"event sourcing과 cqrs 패턴   이벤트 기반 아키텍처 설계 실전 가이드 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Event Sourcing과 CQRS 패턴 - 이벤트 기반 아키텍처 설계 실전 가이드</h1>\n<p>Event Sourcing은 애플리케이션의 상태 변경을 일련의 이벤트로 저장하는 패턴이다. CQRS(Command Query Responsibility Segregation)는 명령(쓰기)과 쿼리(읽기)를 분리하는 패턴이다. 이 두 패턴을 결합하면 확장 가능하고 유연한 이벤트 기반 아키텍처를 구축할 수 있다.</p>\n<h2 id=\"1-event-sourcing과-cqrs-개요\" style=\"position:relative;\"><a href=\"#1-event-sourcing%EA%B3%BC-cqrs-%EA%B0%9C%EC%9A%94\" aria-label=\"1 event sourcing과 cqrs 개요 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1. Event Sourcing과 CQRS 개요</h2>\n<h3 id=\"1-1-event-sourcing이란\" style=\"position:relative;\"><a href=\"#1-1-event-sourcing%EC%9D%B4%EB%9E%80\" aria-label=\"1 1 event sourcing이란 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1-1. Event Sourcing이란?</h3>\n<p>Event Sourcing은 다음과 같은 특징을 가진다:</p>\n<ul>\n<li><strong>이벤트 저장</strong>: 상태 변경을 이벤트로 저장</li>\n<li><strong>이벤트 리플레이</strong>: 이벤트를 재생하여 현재 상태 복원</li>\n<li><strong>완전한 감사 추적</strong>: 모든 변경 이벤트 기록</li>\n<li><strong>시간 여행</strong>: 과거 시점의 상태 조회 가능</li>\n<li><strong>이벤트 재처리</strong>: 읽기 모델 재구축 가능</li>\n</ul>\n<h3 id=\"1-2-cqrs란\" style=\"position:relative;\"><a href=\"#1-2-cqrs%EB%9E%80\" aria-label=\"1 2 cqrs란 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1-2. CQRS란?</h3>\n<p>CQRS는 다음과 같은 특징을 가진다:</p>\n<ul>\n<li><strong>명령과 쿼리 분리</strong>: 쓰기와 읽기 모델 분리</li>\n<li><strong>독립적 스케일링</strong>: 읽기/쓰기 각각 독립적으로 스케일링</li>\n<li><strong>최적화</strong>: 읽기 모델을 쿼리에 최적화</li>\n<li><strong>복잡도 관리</strong>: 도메인 로직과 쿼리 로직 분리</li>\n</ul>\n<h3 id=\"1-3-전통적-방식-vs-event-sourcing--cqrs\" style=\"position:relative;\"><a href=\"#1-3-%EC%A0%84%ED%86%B5%EC%A0%81-%EB%B0%A9%EC%8B%9D-vs-event-sourcing--cqrs\" aria-label=\"1 3 전통적 방식 vs event sourcing  cqrs permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1-3. 전통적 방식 vs Event Sourcing + CQRS</h3>\n<table>\n<thead>\n<tr>\n<th>구분</th>\n<th>전통적 방식</th>\n<th>Event Sourcing + CQRS</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>데이터 저장</strong></td>\n<td>현재 상태만 저장</td>\n<td>모든 이벤트 저장</td>\n</tr>\n<tr>\n<td><strong>감사 추적</strong></td>\n<td>별도 로그 필요</td>\n<td>이벤트가 감사 추적</td>\n</tr>\n<tr>\n<td><strong>읽기 성능</strong></td>\n<td>단일 모델</td>\n<td>최적화된 읽기 모델</td>\n</tr>\n<tr>\n<td><strong>복잡도</strong></td>\n<td>낮음</td>\n<td>높음</td>\n</tr>\n<tr>\n<td><strong>시간 여행</strong></td>\n<td>불가능</td>\n<td>가능</td>\n</tr>\n<tr>\n<td><strong>이벤트 재처리</strong></td>\n<td>불가능</td>\n<td>가능</td>\n</tr>\n</tbody>\n</table>\n<h2 id=\"2-기본-개념과-아키텍처\" style=\"position:relative;\"><a href=\"#2-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98\" aria-label=\"2 기본 개념과 아키텍처 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2. 기본 개념과 아키텍처</h2>\n<h3 id=\"2-1-이벤트-정의\" style=\"position:relative;\"><a href=\"#2-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%95%EC%9D%98\" aria-label=\"2 1 이벤트 정의 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2-1. 이벤트 정의</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// BaseEvent.java</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">abstract</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">BaseEvent</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">Long</span> version<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">LocalDateTime</span> occurredAt<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> eventType<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>aggregateId <span class=\"token operator\">=</span> aggregateId<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>occurredAt <span class=\"token operator\">=</span> <span class=\"token class-name\">LocalDateTime</span><span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>eventType <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">getClass</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">getSimpleName</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token comment\">// getters and setters</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// OrderCreatedEvent.java</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderCreatedEvent</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">BaseEvent</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderItem</span><span class=\"token punctuation\">></span></span> items<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">BigDecimal</span> totalAmount<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">OrderCreatedEvent</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> orderId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">,</span> \n                            <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderItem</span><span class=\"token punctuation\">></span></span> items<span class=\"token punctuation\">,</span> <span class=\"token class-name\">BigDecimal</span> totalAmount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>orderId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>userId <span class=\"token operator\">=</span> userId<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>items <span class=\"token operator\">=</span> items<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>totalAmount <span class=\"token operator\">=</span> totalAmount<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token comment\">// getters and setters</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// OrderPaidEvent.java</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderPaidEvent</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">BaseEvent</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> paymentId<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">BigDecimal</span> amount<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">LocalDateTime</span> paidAt<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">OrderPaidEvent</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> orderId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">String</span> paymentId<span class=\"token punctuation\">,</span> \n                         <span class=\"token class-name\">BigDecimal</span> amount<span class=\"token punctuation\">,</span> <span class=\"token class-name\">LocalDateTime</span> paidAt<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span>orderId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>paymentId <span class=\"token operator\">=</span> paymentId<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>amount <span class=\"token operator\">=</span> amount<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>paidAt <span class=\"token operator\">=</span> paidAt<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token comment\">// getters and setters</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"2-2-aggregate-root\" style=\"position:relative;\"><a href=\"#2-2-aggregate-root\" aria-label=\"2 2 aggregate root permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2-2. Aggregate Root</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// Order.java (Aggregate Root)</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">Order</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> id<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderItem</span><span class=\"token punctuation\">></span></span> items<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">BigDecimal</span> totalAmount<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderStatus</span> status<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">Long</span> version<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> uncommittedEvents <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ArrayList</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n    <span class=\"token comment\">// 이벤트로부터 Aggregate 재구성</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">static</span> <span class=\"token class-name\">Order</span> <span class=\"token function\">fromEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">Order</span> order <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Order</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event <span class=\"token operator\">:</span> events<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            order<span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword\">return</span> order<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token comment\">// 이벤트 적용</span>\n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">void</span> <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>event <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">OrderCreatedEvent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCreatedEvent</span><span class=\"token punctuation\">)</span> event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>event <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">OrderPaidEvent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderPaidEvent</span><span class=\"token punctuation\">)</span> event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>event <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">OrderCancelledEvent</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCancelledEvent</span><span class=\"token punctuation\">)</span> event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>version <span class=\"token operator\">=</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getVersion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">void</span> <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCreatedEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">=</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>userId <span class=\"token operator\">=</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>items <span class=\"token operator\">=</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>totalAmount <span class=\"token operator\">=</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getTotalAmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>status <span class=\"token operator\">=</span> <span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">CREATED</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">void</span> <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderPaidEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>status <span class=\"token operator\">=</span> <span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">PAID</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">void</span> <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCancelledEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>status <span class=\"token operator\">=</span> <span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">CANCELLED</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token comment\">// 명령 처리</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderItem</span><span class=\"token punctuation\">></span></span> items<span class=\"token punctuation\">,</span> <span class=\"token class-name\">BigDecimal</span> totalAmount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IllegalStateException</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Order already created\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        \n        <span class=\"token class-name\">OrderCreatedEvent</span> event <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderCreatedEvent</span><span class=\"token punctuation\">(</span>\n            <span class=\"token constant\">UUID</span><span class=\"token punctuation\">.</span><span class=\"token function\">randomUUID</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            userId<span class=\"token punctuation\">,</span>\n            items<span class=\"token punctuation\">,</span>\n            totalAmount\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        uncommittedEvents<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">pay</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> paymentId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">BigDecimal</span> amount<span class=\"token punctuation\">,</span> <span class=\"token class-name\">LocalDateTime</span> paidAt<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>status <span class=\"token operator\">!=</span> <span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">CREATED</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IllegalStateException</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Order cannot be paid\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        \n        <span class=\"token class-name\">OrderPaidEvent</span> event <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderPaidEvent</span><span class=\"token punctuation\">(</span>\n            <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">,</span>\n            paymentId<span class=\"token punctuation\">,</span>\n            amount<span class=\"token punctuation\">,</span>\n            paidAt\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        uncommittedEvents<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">cancel</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> reason<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>status <span class=\"token operator\">==</span> <span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">PAID</span> <span class=\"token operator\">||</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>status <span class=\"token operator\">==</span> <span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">CANCELLED</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IllegalStateException</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Order cannot be cancelled\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        \n        <span class=\"token class-name\">OrderCancelledEvent</span> event <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderCancelledEvent</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">,</span> reason<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        uncommittedEvents<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getUncommittedEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ArrayList</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">(</span>uncommittedEvents<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">markEventsAsCommitted</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        uncommittedEvents<span class=\"token punctuation\">.</span><span class=\"token function\">clear</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token comment\">// getters</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"3-이벤트-스토어-구현\" style=\"position:relative;\"><a href=\"#3-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%8A%A4%ED%86%A0%EC%96%B4-%EA%B5%AC%ED%98%84\" aria-label=\"3 이벤트 스토어 구현 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3. 이벤트 스토어 구현</h2>\n<h3 id=\"3-1-이벤트-스토어-인터페이스\" style=\"position:relative;\"><a href=\"#3-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%8A%A4%ED%86%A0%EC%96%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4\" aria-label=\"3 1 이벤트 스토어 인터페이스 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3-1. 이벤트 스토어 인터페이스</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// EventStore.java</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\">EventStore</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">void</span> <span class=\"token function\">saveEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Long</span> expectedVersion<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Long</span> fromVersion<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getAllEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getEventsByType</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> eventType<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"3-2-jpa-기반-이벤트-스토어\" style=\"position:relative;\"><a href=\"#3-2-jpa-%EA%B8%B0%EB%B0%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%8A%A4%ED%86%A0%EC%96%B4\" aria-label=\"3 2 jpa 기반 이벤트 스토어 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3-2. JPA 기반 이벤트 스토어</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// EventEntity.java</span>\n<span class=\"token annotation punctuation\">@Entity</span>\n<span class=\"token annotation punctuation\">@Table</span><span class=\"token punctuation\">(</span>name <span class=\"token operator\">=</span> <span class=\"token string\">\"events\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">EventEntity</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token annotation punctuation\">@Id</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> id<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">Long</span> version<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> eventType<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> columnDefinition <span class=\"token operator\">=</span> <span class=\"token string\">\"TEXT\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> eventData<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">LocalDateTime</span> occurredAt<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token comment\">// getters and setters</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// JpaEventStore.java</span>\n<span class=\"token annotation punctuation\">@Repository</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">JpaEventStore</span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\">EventStore</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">EventRepository</span> eventRepository<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">ObjectMapper</span> objectMapper<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Override</span>\n    <span class=\"token annotation punctuation\">@Transactional</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">saveEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Long</span> expectedVersion<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 낙관적 잠금 확인</span>\n        <span class=\"token class-name\">Long</span> currentVersion <span class=\"token operator\">=</span> eventRepository<span class=\"token punctuation\">.</span><span class=\"token function\">findMaxVersionByAggregateId</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">orElse</span><span class=\"token punctuation\">(</span><span class=\"token number\">0L</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>expectedVersion <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token operator\">!</span>currentVersion<span class=\"token punctuation\">.</span><span class=\"token function\">equals</span><span class=\"token punctuation\">(</span>expectedVersion<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ConcurrentModificationException</span><span class=\"token punctuation\">(</span>\n                <span class=\"token string\">\"Expected version \"</span> <span class=\"token operator\">+</span> expectedVersion <span class=\"token operator\">+</span> <span class=\"token string\">\" but was \"</span> <span class=\"token operator\">+</span> currentVersion\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        \n        <span class=\"token class-name\">Long</span> nextVersion <span class=\"token operator\">=</span> currentVersion<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event <span class=\"token operator\">:</span> events<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            nextVersion<span class=\"token operator\">++</span><span class=\"token punctuation\">;</span>\n            event<span class=\"token punctuation\">.</span><span class=\"token function\">setVersion</span><span class=\"token punctuation\">(</span>nextVersion<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            \n            <span class=\"token class-name\">EventEntity</span> entity <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">EventEntity</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            entity<span class=\"token punctuation\">.</span><span class=\"token function\">setId</span><span class=\"token punctuation\">(</span><span class=\"token constant\">UUID</span><span class=\"token punctuation\">.</span><span class=\"token function\">randomUUID</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            entity<span class=\"token punctuation\">.</span><span class=\"token function\">setAggregateId</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            entity<span class=\"token punctuation\">.</span><span class=\"token function\">setVersion</span><span class=\"token punctuation\">(</span>nextVersion<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            entity<span class=\"token punctuation\">.</span><span class=\"token function\">setEventType</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getClass</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">getSimpleName</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            entity<span class=\"token punctuation\">.</span><span class=\"token function\">setEventData</span><span class=\"token punctuation\">(</span><span class=\"token function\">serializeEvent</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            entity<span class=\"token punctuation\">.</span><span class=\"token function\">setOccurredAt</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getOccurredAt</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            \n            eventRepository<span class=\"token punctuation\">.</span><span class=\"token function\">save</span><span class=\"token punctuation\">(</span>entity<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@Override</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">EventEntity</span><span class=\"token punctuation\">></span></span> entities <span class=\"token operator\">=</span> eventRepository\n            <span class=\"token punctuation\">.</span><span class=\"token function\">findByAggregateIdOrderByVersionAsc</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">deserializeEvents</span><span class=\"token punctuation\">(</span>entities<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@Override</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Long</span> fromVersion<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">EventEntity</span><span class=\"token punctuation\">></span></span> entities <span class=\"token operator\">=</span> eventRepository\n            <span class=\"token punctuation\">.</span><span class=\"token function\">findByAggregateIdAndVersionGreaterThanOrderByVersionAsc</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">,</span> fromVersion<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">deserializeEvents</span><span class=\"token punctuation\">(</span>entities<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> <span class=\"token function\">serializeEvent</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> objectMapper<span class=\"token punctuation\">.</span><span class=\"token function\">writeValueAsString</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">JsonProcessingException</span> e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">RuntimeException</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Failed to serialize event\"</span><span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">BaseEvent</span> <span class=\"token function\">deserializeEvent</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEntity</span> entity<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\">Class</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token operator\">?</span><span class=\"token punctuation\">></span></span> eventClass <span class=\"token operator\">=</span> <span class=\"token class-name\">Class</span><span class=\"token punctuation\">.</span><span class=\"token function\">forName</span><span class=\"token punctuation\">(</span>\n                <span class=\"token string\">\"com.example.events.\"</span> <span class=\"token operator\">+</span> entity<span class=\"token punctuation\">.</span><span class=\"token function\">getEventType</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">)</span> objectMapper<span class=\"token punctuation\">.</span><span class=\"token function\">readValue</span><span class=\"token punctuation\">(</span>\n                entity<span class=\"token punctuation\">.</span><span class=\"token function\">getEventData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                eventClass\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">Exception</span> e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">RuntimeException</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Failed to deserialize event\"</span><span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">deserializeEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">EventEntity</span><span class=\"token punctuation\">></span></span> entities<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> entities<span class=\"token punctuation\">.</span><span class=\"token function\">stream</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token operator\">::</span><span class=\"token function\">deserializeEvent</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">collect</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Collectors</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"3-3-repository-패턴\" style=\"position:relative;\"><a href=\"#3-3-repository-%ED%8C%A8%ED%84%B4\" aria-label=\"3 3 repository 패턴 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3-3. Repository 패턴</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// OrderRepository.java</span>\n<span class=\"token annotation punctuation\">@Repository</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderRepository</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">EventStore</span> eventStore<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">Order</span> <span class=\"token function\">findById</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> orderId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events <span class=\"token operator\">=</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">getEvents</span><span class=\"token punctuation\">(</span>orderId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>events<span class=\"token punctuation\">.</span><span class=\"token function\">isEmpty</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword\">return</span> <span class=\"token class-name\">Order</span><span class=\"token punctuation\">.</span><span class=\"token function\">fromEvents</span><span class=\"token punctuation\">(</span>events<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">save</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Order</span> order<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> uncommittedEvents <span class=\"token operator\">=</span> order<span class=\"token punctuation\">.</span><span class=\"token function\">getUncommittedEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>uncommittedEvents<span class=\"token punctuation\">.</span><span class=\"token function\">isEmpty</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\">Long</span> expectedVersion <span class=\"token operator\">=</span> order<span class=\"token punctuation\">.</span><span class=\"token function\">getVersion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> uncommittedEvents<span class=\"token punctuation\">.</span><span class=\"token function\">size</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">saveEvents</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> uncommittedEvents<span class=\"token punctuation\">,</span> expectedVersion<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            order<span class=\"token punctuation\">.</span><span class=\"token function\">markEventsAsCommitted</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"4-cqrs-구현\" style=\"position:relative;\"><a href=\"#4-cqrs-%EA%B5%AC%ED%98%84\" aria-label=\"4 cqrs 구현 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4. CQRS 구현</h2>\n<h3 id=\"4-1-명령-처리-write-side\" style=\"position:relative;\"><a href=\"#4-1-%EB%AA%85%EB%A0%B9-%EC%B2%98%EB%A6%AC-write-side\" aria-label=\"4 1 명령 처리 write side permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4-1. 명령 처리 (Write Side)</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// CreateOrderCommand.java</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">CreateOrderCommand</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderItem</span><span class=\"token punctuation\">></span></span> items<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">BigDecimal</span> totalAmount<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token comment\">// getters and setters</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// OrderCommandHandler.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderCommandHandler</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderRepository</span> orderRepository<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">EventPublisher</span> eventPublisher<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">String</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">CreateOrderCommand</span> command<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">Order</span> order <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Order</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        order<span class=\"token punctuation\">.</span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span>\n            command<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            command<span class=\"token punctuation\">.</span><span class=\"token function\">getItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            command<span class=\"token punctuation\">.</span><span class=\"token function\">getTotalAmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        orderRepository<span class=\"token punctuation\">.</span><span class=\"token function\">save</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token comment\">// 이벤트 발행</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event <span class=\"token operator\">:</span> order<span class=\"token punctuation\">.</span><span class=\"token function\">getUncommittedEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            eventPublisher<span class=\"token punctuation\">.</span><span class=\"token function\">publish</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        \n        <span class=\"token keyword\">return</span> order<span class=\"token punctuation\">.</span><span class=\"token function\">getId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">PayOrderCommand</span> command<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">Order</span> order <span class=\"token operator\">=</span> orderRepository<span class=\"token punctuation\">.</span><span class=\"token function\">findById</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">.</span><span class=\"token function\">getOrderId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>order <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderNotFoundException</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">.</span><span class=\"token function\">getOrderId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        \n        order<span class=\"token punctuation\">.</span><span class=\"token function\">pay</span><span class=\"token punctuation\">(</span>\n            command<span class=\"token punctuation\">.</span><span class=\"token function\">getPaymentId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            command<span class=\"token punctuation\">.</span><span class=\"token function\">getAmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token class-name\">LocalDateTime</span><span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        orderRepository<span class=\"token punctuation\">.</span><span class=\"token function\">save</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event <span class=\"token operator\">:</span> order<span class=\"token punctuation\">.</span><span class=\"token function\">getUncommittedEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            eventPublisher<span class=\"token punctuation\">.</span><span class=\"token function\">publish</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"4-2-읽기-모델-read-side\" style=\"position:relative;\"><a href=\"#4-2-%EC%9D%BD%EA%B8%B0-%EB%AA%A8%EB%8D%B8-read-side\" aria-label=\"4 2 읽기 모델 read side permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4-2. 읽기 모델 (Read Side)</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// OrderReadModel.java</span>\n<span class=\"token annotation punctuation\">@Entity</span>\n<span class=\"token annotation punctuation\">@Table</span><span class=\"token punctuation\">(</span>name <span class=\"token operator\">=</span> <span class=\"token string\">\"order_read_model\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderReadModel</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token annotation punctuation\">@Id</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> id<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@ElementCollection</span>\n    <span class=\"token annotation punctuation\">@CollectionTable</span><span class=\"token punctuation\">(</span>name <span class=\"token operator\">=</span> <span class=\"token string\">\"order_items_read\"</span><span class=\"token punctuation\">,</span> joinColumns <span class=\"token operator\">=</span> <span class=\"token annotation punctuation\">@JoinColumn</span><span class=\"token punctuation\">(</span>name <span class=\"token operator\">=</span> <span class=\"token string\">\"order_id\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderItem</span><span class=\"token punctuation\">></span></span> items<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">BigDecimal</span> totalAmount<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Enumerated</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EnumType</span><span class=\"token punctuation\">.</span><span class=\"token constant\">STRING</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderStatus</span> status<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">LocalDateTime</span> createdAt<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">LocalDateTime</span> paidAt<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token comment\">// getters and setters</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// OrderReadRepository.java</span>\n<span class=\"token annotation punctuation\">@Repository</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\">OrderReadRepository</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">JpaRepository</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">,</span> <span class=\"token class-name\">String</span><span class=\"token punctuation\">></span></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">findByUserId</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">findByStatus</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderStatus</span> status<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">findByCreatedAtBetween</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">LocalDateTime</span> start<span class=\"token punctuation\">,</span> <span class=\"token class-name\">LocalDateTime</span> end<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"4-3-이벤트-핸들러-읽기-모델-업데이트\" style=\"position:relative;\"><a href=\"#4-3-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%95%B8%EB%93%A4%EB%9F%AC-%EC%9D%BD%EA%B8%B0-%EB%AA%A8%EB%8D%B8-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8\" aria-label=\"4 3 이벤트 핸들러 읽기 모델 업데이트 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4-3. 이벤트 핸들러 (읽기 모델 업데이트)</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// OrderReadModelUpdater.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderReadModelUpdater</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderReadRepository</span> readRepository<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCreatedEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">OrderReadModel</span> readModel <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setId</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setUserId</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setItems</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setTotalAmount</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getTotalAmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setStatus</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">CREATED</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setCreatedAt</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getOccurredAt</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">save</span><span class=\"token punctuation\">(</span>readModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderPaidEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">OrderReadModel</span> readModel <span class=\"token operator\">=</span> readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">findById</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">orElseThrow</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderNotFoundException</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setStatus</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">PAID</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setPaidAt</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getPaidAt</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">save</span><span class=\"token punctuation\">(</span>readModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCancelledEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">OrderReadModel</span> readModel <span class=\"token operator\">=</span> readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">findById</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">orElseThrow</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderNotFoundException</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        readModel<span class=\"token punctuation\">.</span><span class=\"token function\">setStatus</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderStatus</span><span class=\"token punctuation\">.</span><span class=\"token constant\">CANCELLED</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">save</span><span class=\"token punctuation\">(</span>readModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"4-4-쿼리-처리-read-side\" style=\"position:relative;\"><a href=\"#4-4-%EC%BF%BC%EB%A6%AC-%EC%B2%98%EB%A6%AC-read-side\" aria-label=\"4 4 쿼리 처리 read side permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4-4. 쿼리 처리 (Read Side)</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// OrderQueryService.java</span>\n<span class=\"token annotation punctuation\">@Service</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderQueryService</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderReadRepository</span> readRepository<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">OrderReadModel</span> <span class=\"token function\">getOrder</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> orderId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">findById</span><span class=\"token punctuation\">(</span>orderId<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">orElseThrow</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderNotFoundException</span><span class=\"token punctuation\">(</span>orderId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getOrdersByUser</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">findByUserId</span><span class=\"token punctuation\">(</span>userId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getOrdersByStatus</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderStatus</span> status<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">findByStatus</span><span class=\"token punctuation\">(</span>status<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getOrdersByDateRange</span><span class=\"token punctuation\">(</span>\n        <span class=\"token class-name\">LocalDateTime</span> start<span class=\"token punctuation\">,</span>\n        <span class=\"token class-name\">LocalDateTime</span> end\n    <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">findByCreatedAtBetween</span><span class=\"token punctuation\">(</span>start<span class=\"token punctuation\">,</span> end<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"5-스냅샷-패턴\" style=\"position:relative;\"><a href=\"#5-%EC%8A%A4%EB%83%85%EC%83%B7-%ED%8C%A8%ED%84%B4\" aria-label=\"5 스냅샷 패턴 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>5. 스냅샷 패턴</h2>\n<h3 id=\"5-1-스냅샷-구현\" style=\"position:relative;\"><a href=\"#5-1-%EC%8A%A4%EB%83%85%EC%83%B7-%EA%B5%AC%ED%98%84\" aria-label=\"5 1 스냅샷 구현 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>5-1. 스냅샷 구현</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// Snapshot.java</span>\n<span class=\"token annotation punctuation\">@Entity</span>\n<span class=\"token annotation punctuation\">@Table</span><span class=\"token punctuation\">(</span>name <span class=\"token operator\">=</span> <span class=\"token string\">\"snapshots\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">Snapshot</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token annotation punctuation\">@Id</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> id<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">Long</span> version<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> columnDefinition <span class=\"token operator\">=</span> <span class=\"token string\">\"TEXT\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> snapshotData<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Column</span><span class=\"token punctuation\">(</span>nullable <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">LocalDateTime</span> createdAt<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token comment\">// getters and setters</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// SnapshotRepository.java</span>\n<span class=\"token annotation punctuation\">@Repository</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\">SnapshotRepository</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">JpaRepository</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Snapshot</span><span class=\"token punctuation\">,</span> <span class=\"token class-name\">String</span><span class=\"token punctuation\">></span></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token class-name\">Optional</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Snapshot</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">findTopByAggregateIdOrderByVersionDesc</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// SnapshotService.java</span>\n<span class=\"token annotation punctuation\">@Service</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">SnapshotService</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">SnapshotRepository</span> snapshotRepository<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">EventStore</span> eventStore<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">ObjectMapper</span> objectMapper<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">static</span> <span class=\"token keyword\">final</span> <span class=\"token keyword\">int</span> <span class=\"token constant\">SNAPSHOT_INTERVAL</span> <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 100개 이벤트마다 스냅샷</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">saveSnapshot</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Order</span> order<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getVersion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">%</span> <span class=\"token constant\">SNAPSHOT_INTERVAL</span> <span class=\"token operator\">==</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\">Snapshot</span> snapshot <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Snapshot</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            snapshot<span class=\"token punctuation\">.</span><span class=\"token function\">setId</span><span class=\"token punctuation\">(</span><span class=\"token constant\">UUID</span><span class=\"token punctuation\">.</span><span class=\"token function\">randomUUID</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            snapshot<span class=\"token punctuation\">.</span><span class=\"token function\">setAggregateId</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            snapshot<span class=\"token punctuation\">.</span><span class=\"token function\">setVersion</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getVersion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            snapshot<span class=\"token punctuation\">.</span><span class=\"token function\">setSnapshotData</span><span class=\"token punctuation\">(</span><span class=\"token function\">serializeOrder</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            snapshot<span class=\"token punctuation\">.</span><span class=\"token function\">setCreatedAt</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">LocalDateTime</span><span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            \n            snapshotRepository<span class=\"token punctuation\">.</span><span class=\"token function\">save</span><span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">Order</span> <span class=\"token function\">loadFromSnapshot</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">Optional</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Snapshot</span><span class=\"token punctuation\">></span></span> snapshotOpt <span class=\"token operator\">=</span> snapshotRepository\n            <span class=\"token punctuation\">.</span><span class=\"token function\">findTopByAggregateIdOrderByVersionDesc</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>snapshotOpt<span class=\"token punctuation\">.</span><span class=\"token function\">isPresent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\">Snapshot</span> snapshot <span class=\"token operator\">=</span> snapshotOpt<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token class-name\">Order</span> order <span class=\"token operator\">=</span> <span class=\"token function\">deserializeOrder</span><span class=\"token punctuation\">(</span>snapshot<span class=\"token punctuation\">.</span><span class=\"token function\">getSnapshotData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            \n            <span class=\"token comment\">// 스냅샷 이후의 이벤트 로드</span>\n            <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events <span class=\"token operator\">=</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">getEvents</span><span class=\"token punctuation\">(</span>\n                aggregateId<span class=\"token punctuation\">,</span>\n                snapshot<span class=\"token punctuation\">.</span><span class=\"token function\">getVersion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            \n            <span class=\"token comment\">// 이벤트 적용</span>\n            <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event <span class=\"token operator\">:</span> events<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                order<span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            \n            <span class=\"token keyword\">return</span> order<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// 스냅샷이 없으면 모든 이벤트 로드</span>\n            <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events <span class=\"token operator\">=</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">getEvents</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">return</span> <span class=\"token class-name\">Order</span><span class=\"token punctuation\">.</span><span class=\"token function\">fromEvents</span><span class=\"token punctuation\">(</span>events<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> <span class=\"token function\">serializeOrder</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Order</span> order<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> objectMapper<span class=\"token punctuation\">.</span><span class=\"token function\">writeValueAsString</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">JsonProcessingException</span> e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">RuntimeException</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Failed to serialize order\"</span><span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">Order</span> <span class=\"token function\">deserializeOrder</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> data<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> objectMapper<span class=\"token punctuation\">.</span><span class=\"token function\">readValue</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Order</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">class</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">JsonProcessingException</span> e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">RuntimeException</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Failed to deserialize order\"</span><span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"6-이벤트-발행과-구독\" style=\"position:relative;\"><a href=\"#6-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%9C%ED%96%89%EA%B3%BC-%EA%B5%AC%EB%8F%85\" aria-label=\"6 이벤트 발행과 구독 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>6. 이벤트 발행과 구독</h2>\n<h3 id=\"6-1-이벤트-발행자\" style=\"position:relative;\"><a href=\"#6-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%9C%ED%96%89%EC%9E%90\" aria-label=\"6 1 이벤트 발행자 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>6-1. 이벤트 발행자</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// EventPublisher.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">EventPublisher</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">final</span> <span class=\"token class-name\">ApplicationEventPublisher</span> applicationEventPublisher<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">EventPublisher</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">ApplicationEventPublisher</span> applicationEventPublisher<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>applicationEventPublisher <span class=\"token operator\">=</span> applicationEventPublisher<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">publish</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        applicationEventPublisher<span class=\"token punctuation\">.</span><span class=\"token function\">publishEvent</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">publishAll</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event <span class=\"token operator\">:</span> events<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">publish</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"6-2-외부-시스템-이벤트-발행-kafka\" style=\"position:relative;\"><a href=\"#6-2-%EC%99%B8%EB%B6%80-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%9C%ED%96%89-kafka\" aria-label=\"6 2 외부 시스템 이벤트 발행 kafka permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>6-2. 외부 시스템 이벤트 발행 (Kafka)</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// KafkaEventPublisher.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">KafkaEventPublisher</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">KafkaTemplate</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">String</span><span class=\"token punctuation\">,</span> <span class=\"token class-name\">Object</span><span class=\"token punctuation\">></span></span> kafkaTemplate<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token annotation punctuation\">@Async</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">String</span> topic <span class=\"token operator\">=</span> <span class=\"token string\">\"domain-events\"</span><span class=\"token punctuation\">;</span>\n        kafkaTemplate<span class=\"token punctuation\">.</span><span class=\"token function\">send</span><span class=\"token punctuation\">(</span>topic<span class=\"token punctuation\">,</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"6-3-이벤트-구독자\" style=\"position:relative;\"><a href=\"#6-3-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B5%AC%EB%8F%85%EC%9E%90\" aria-label=\"6 3 이벤트 구독자 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>6-3. 이벤트 구독자</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// InventoryEventHandler.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">InventoryEventHandler</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">InventoryService</span> inventoryService<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token annotation punctuation\">@Async</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCreatedEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 재고 차감</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderItem</span> item <span class=\"token operator\">:</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            inventoryService<span class=\"token punctuation\">.</span><span class=\"token function\">decreaseStock</span><span class=\"token punctuation\">(</span>\n                item<span class=\"token punctuation\">.</span><span class=\"token function\">getProductId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                item<span class=\"token punctuation\">.</span><span class=\"token function\">getQuantity</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token annotation punctuation\">@Async</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCancelledEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 재고 복구</span>\n        <span class=\"token class-name\">Order</span> order <span class=\"token operator\">=</span> <span class=\"token function\">loadOrder</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderItem</span> item <span class=\"token operator\">:</span> order<span class=\"token punctuation\">.</span><span class=\"token function\">getItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            inventoryService<span class=\"token punctuation\">.</span><span class=\"token function\">increaseStock</span><span class=\"token punctuation\">(</span>\n                item<span class=\"token punctuation\">.</span><span class=\"token function\">getProductId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                item<span class=\"token punctuation\">.</span><span class=\"token function\">getQuantity</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// NotificationEventHandler.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">NotificationEventHandler</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">NotificationService</span> notificationService<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token annotation punctuation\">@Async</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCreatedEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        notificationService<span class=\"token punctuation\">.</span><span class=\"token function\">sendOrderConfirmation</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token annotation punctuation\">@Async</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderPaidEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">Order</span> order <span class=\"token operator\">=</span> <span class=\"token function\">loadOrder</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        notificationService<span class=\"token punctuation\">.</span><span class=\"token function\">sendPaymentConfirmation</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"7-spring-boot-통합\" style=\"position:relative;\"><a href=\"#7-spring-boot-%ED%86%B5%ED%95%A9\" aria-label=\"7 spring boot 통합 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>7. Spring Boot 통합</h2>\n<h3 id=\"7-1-설정\" style=\"position:relative;\"><a href=\"#7-1-%EC%84%A4%EC%A0%95\" aria-label=\"7 1 설정 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>7-1. 설정</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// EventSourcingConfig.java</span>\n<span class=\"token annotation punctuation\">@Configuration</span>\n<span class=\"token annotation punctuation\">@EnableAsync</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">EventSourcingConfig</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Bean</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">ObjectMapper</span> <span class=\"token function\">objectMapper</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">ObjectMapper</span> mapper <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ObjectMapper</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        mapper<span class=\"token punctuation\">.</span><span class=\"token function\">registerModule</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">JavaTimeModule</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        mapper<span class=\"token punctuation\">.</span><span class=\"token function\">configure</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">DeserializationFeature</span><span class=\"token punctuation\">.</span><span class=\"token constant\">FAIL_ON_UNKNOWN_PROPERTIES</span><span class=\"token punctuation\">,</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> mapper<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@Bean</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">TaskExecutor</span> <span class=\"token function\">eventTaskExecutor</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">ThreadPoolTaskExecutor</span> executor <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ThreadPoolTaskExecutor</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        executor<span class=\"token punctuation\">.</span><span class=\"token function\">setCorePoolSize</span><span class=\"token punctuation\">(</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        executor<span class=\"token punctuation\">.</span><span class=\"token function\">setMaxPoolSize</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        executor<span class=\"token punctuation\">.</span><span class=\"token function\">setQueueCapacity</span><span class=\"token punctuation\">(</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        executor<span class=\"token punctuation\">.</span><span class=\"token function\">setThreadNamePrefix</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"event-handler-\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        executor<span class=\"token punctuation\">.</span><span class=\"token function\">initialize</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> executor<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"7-2-rest-api\" style=\"position:relative;\"><a href=\"#7-2-rest-api\" aria-label=\"7 2 rest api permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>7-2. REST API</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// OrderController.java</span>\n<span class=\"token annotation punctuation\">@RestController</span>\n<span class=\"token annotation punctuation\">@RequestMapping</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/api/orders\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderController</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderCommandHandler</span> commandHandler<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderQueryService</span> queryService<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@PostMapping</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">ResponseEntity</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderResponse</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">createOrder</span><span class=\"token punctuation\">(</span><span class=\"token annotation punctuation\">@RequestBody</span> <span class=\"token class-name\">CreateOrderRequest</span> request<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">CreateOrderCommand</span> command <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">CreateOrderCommand</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        command<span class=\"token punctuation\">.</span><span class=\"token function\">setUserId</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        command<span class=\"token punctuation\">.</span><span class=\"token function\">setItems</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span><span class=\"token function\">getItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        command<span class=\"token punctuation\">.</span><span class=\"token function\">setTotalAmount</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span><span class=\"token function\">getTotalAmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token class-name\">String</span> orderId <span class=\"token operator\">=</span> commandHandler<span class=\"token punctuation\">.</span><span class=\"token function\">handle</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token class-name\">OrderReadModel</span> order <span class=\"token operator\">=</span> queryService<span class=\"token punctuation\">.</span><span class=\"token function\">getOrder</span><span class=\"token punctuation\">(</span>orderId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token class-name\">ResponseEntity</span><span class=\"token punctuation\">.</span><span class=\"token function\">ok</span><span class=\"token punctuation\">(</span><span class=\"token function\">toResponse</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@PostMapping</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/{orderId}/pay\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">ResponseEntity</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Void</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">payOrder</span><span class=\"token punctuation\">(</span>\n        <span class=\"token annotation punctuation\">@PathVariable</span> <span class=\"token class-name\">String</span> orderId<span class=\"token punctuation\">,</span>\n        <span class=\"token annotation punctuation\">@RequestBody</span> <span class=\"token class-name\">PayOrderRequest</span> request\n    <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">PayOrderCommand</span> command <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">PayOrderCommand</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        command<span class=\"token punctuation\">.</span><span class=\"token function\">setOrderId</span><span class=\"token punctuation\">(</span>orderId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        command<span class=\"token punctuation\">.</span><span class=\"token function\">setPaymentId</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span><span class=\"token function\">getPaymentId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        command<span class=\"token punctuation\">.</span><span class=\"token function\">setAmount</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span><span class=\"token function\">getAmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        commandHandler<span class=\"token punctuation\">.</span><span class=\"token function\">handle</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token keyword\">return</span> <span class=\"token class-name\">ResponseEntity</span><span class=\"token punctuation\">.</span><span class=\"token function\">ok</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">build</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@GetMapping</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/{orderId}\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">ResponseEntity</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderResponse</span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getOrder</span><span class=\"token punctuation\">(</span><span class=\"token annotation punctuation\">@PathVariable</span> <span class=\"token class-name\">String</span> orderId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">OrderReadModel</span> order <span class=\"token operator\">=</span> queryService<span class=\"token punctuation\">.</span><span class=\"token function\">getOrder</span><span class=\"token punctuation\">(</span>orderId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token class-name\">ResponseEntity</span><span class=\"token punctuation\">.</span><span class=\"token function\">ok</span><span class=\"token punctuation\">(</span><span class=\"token function\">toResponse</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@GetMapping</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">ResponseEntity</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">List</span><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderResponse</span><span class=\"token punctuation\">></span><span class=\"token punctuation\">></span></span> <span class=\"token function\">getOrders</span><span class=\"token punctuation\">(</span>\n        <span class=\"token annotation punctuation\">@RequestParam</span><span class=\"token punctuation\">(</span>required <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span> <span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">,</span>\n        <span class=\"token annotation punctuation\">@RequestParam</span><span class=\"token punctuation\">(</span>required <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span> <span class=\"token class-name\">OrderStatus</span> status\n    <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderReadModel</span><span class=\"token punctuation\">></span></span> orders<span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>userId <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            orders <span class=\"token operator\">=</span> queryService<span class=\"token punctuation\">.</span><span class=\"token function\">getOrdersByUser</span><span class=\"token punctuation\">(</span>userId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            orders <span class=\"token operator\">=</span> queryService<span class=\"token punctuation\">.</span><span class=\"token function\">getOrdersByStatus</span><span class=\"token punctuation\">(</span>status<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n            orders <span class=\"token operator\">=</span> queryService<span class=\"token punctuation\">.</span><span class=\"token function\">getAllOrders</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        \n        <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderResponse</span><span class=\"token punctuation\">></span></span> responses <span class=\"token operator\">=</span> orders<span class=\"token punctuation\">.</span><span class=\"token function\">stream</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token operator\">::</span><span class=\"token function\">toResponse</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">collect</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Collectors</span><span class=\"token punctuation\">.</span><span class=\"token function\">toList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token keyword\">return</span> <span class=\"token class-name\">ResponseEntity</span><span class=\"token punctuation\">.</span><span class=\"token function\">ok</span><span class=\"token punctuation\">(</span>responses<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderResponse</span> <span class=\"token function\">toResponse</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderReadModel</span> order<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">OrderResponse</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderResponse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">setId</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">setUserId</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">setItems</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">setTotalAmount</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getTotalAmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">setStatus</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getStatus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">setCreatedAt</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">getCreatedAt</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> response<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"8-실전-패턴과-모범-사례\" style=\"position:relative;\"><a href=\"#8-%EC%8B%A4%EC%A0%84-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EB%AA%A8%EB%B2%94-%EC%82%AC%EB%A1%80\" aria-label=\"8 실전 패턴과 모범 사례 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>8. 실전 패턴과 모범 사례</h2>\n<h3 id=\"8-1-이벤트-버전-관리\" style=\"position:relative;\"><a href=\"#8-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC\" aria-label=\"8 1 이벤트 버전 관리 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>8-1. 이벤트 버전 관리</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// OrderCreatedEventV2.java</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderCreatedEventV2</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">BaseEvent</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> userId<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">OrderItem</span><span class=\"token punctuation\">></span></span> items<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">BigDecimal</span> totalAmount<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">String</span> shippingAddress<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 새 필드 추가</span>\n    \n    <span class=\"token comment\">// getters and setters</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// 이벤트 업그레이드</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">EventUpgrader</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">BaseEvent</span> <span class=\"token function\">upgrade</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>event <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">OrderCreatedEvent</span> <span class=\"token operator\">&amp;&amp;</span> \n            event<span class=\"token punctuation\">.</span><span class=\"token function\">getVersion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// V1을 V2로 업그레이드</span>\n            <span class=\"token class-name\">OrderCreatedEvent</span> v1 <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCreatedEvent</span><span class=\"token punctuation\">)</span> event<span class=\"token punctuation\">;</span>\n            <span class=\"token class-name\">OrderCreatedEventV2</span> v2 <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OrderCreatedEventV2</span><span class=\"token punctuation\">(</span>\n                v1<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                v1<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                v1<span class=\"token punctuation\">.</span><span class=\"token function\">getItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                v1<span class=\"token punctuation\">.</span><span class=\"token function\">getTotalAmount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                <span class=\"token string\">\"\"</span> <span class=\"token comment\">// 기본값</span>\n            <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            v2<span class=\"token punctuation\">.</span><span class=\"token function\">setVersion</span><span class=\"token punctuation\">(</span><span class=\"token number\">2L</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">return</span> v2<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword\">return</span> event<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"8-2-이벤트-순서-보장\" style=\"position:relative;\"><a href=\"#8-2-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%88%9C%EC%84%9C-%EB%B3%B4%EC%9E%A5\" aria-label=\"8 2 이벤트 순서 보장 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>8-2. 이벤트 순서 보장</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// OrderedEventStore.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderedEventStore</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">EventStore</span> eventStore<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">RedisTemplate</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">String</span><span class=\"token punctuation\">,</span> <span class=\"token class-name\">String</span><span class=\"token punctuation\">></span></span> redisTemplate<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">saveEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">,</span> <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Long</span> expectedVersion<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">String</span> lockKey <span class=\"token operator\">=</span> <span class=\"token string\">\"lock:\"</span> <span class=\"token operator\">+</span> aggregateId<span class=\"token punctuation\">;</span>\n        \n        <span class=\"token comment\">// 분산 락 획득</span>\n        <span class=\"token class-name\">Boolean</span> locked <span class=\"token operator\">=</span> redisTemplate<span class=\"token punctuation\">.</span><span class=\"token function\">opsForValue</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">setIfAbsent</span><span class=\"token punctuation\">(</span>lockKey<span class=\"token punctuation\">,</span> <span class=\"token string\">\"locked\"</span><span class=\"token punctuation\">,</span> <span class=\"token class-name\">Duration</span><span class=\"token punctuation\">.</span><span class=\"token function\">ofSeconds</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>locked<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ConcurrentModificationException</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Failed to acquire lock\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        \n        <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n            eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">saveEvents</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">,</span> events<span class=\"token punctuation\">,</span> expectedVersion<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">finally</span> <span class=\"token punctuation\">{</span>\n            redisTemplate<span class=\"token punctuation\">.</span><span class=\"token function\">delete</span><span class=\"token punctuation\">(</span>lockKey<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"8-3-이벤트-재처리\" style=\"position:relative;\"><a href=\"#8-3-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%9E%AC%EC%B2%98%EB%A6%AC\" aria-label=\"8 3 이벤트 재처리 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>8-3. 이벤트 재처리</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// EventReplayService.java</span>\n<span class=\"token annotation punctuation\">@Service</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">EventReplayService</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">EventStore</span> eventStore<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderReadModelUpdater</span> readModelUpdater<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@Autowired</span>\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">OrderReadRepository</span> readRepository<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">replayEvents</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">String</span> aggregateId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// 읽기 모델 삭제</span>\n        readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">deleteById</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token comment\">// 이벤트 재생</span>\n        <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> events <span class=\"token operator\">=</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">getEvents</span><span class=\"token punctuation\">(</span>aggregateId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event <span class=\"token operator\">:</span> events<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            readModelUpdater<span class=\"token punctuation\">.</span><span class=\"token function\">handle</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">replayAllEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">List</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">BaseEvent</span><span class=\"token punctuation\">></span></span> allEvents <span class=\"token operator\">=</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">getAllEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token comment\">// 읽기 모델 전체 삭제</span>\n        readRepository<span class=\"token punctuation\">.</span><span class=\"token function\">deleteAll</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token comment\">// 이벤트 재생</span>\n        <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event <span class=\"token operator\">:</span> allEvents<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            readModelUpdater<span class=\"token punctuation\">.</span><span class=\"token function\">handle</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"9-모니터링과-디버깅\" style=\"position:relative;\"><a href=\"#9-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81%EA%B3%BC-%EB%94%94%EB%B2%84%EA%B9%85\" aria-label=\"9 모니터링과 디버깅 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>9. 모니터링과 디버깅</h2>\n<h3 id=\"9-1-이벤트-로깅\" style=\"position:relative;\"><a href=\"#9-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A1%9C%EA%B9%85\" aria-label=\"9 1 이벤트 로깅 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>9-1. 이벤트 로깅</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// LoggingEventHandler.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">LoggingEventHandler</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">static</span> <span class=\"token keyword\">final</span> <span class=\"token class-name\">Logger</span> logger <span class=\"token operator\">=</span> <span class=\"token class-name\">LoggerFactory</span><span class=\"token punctuation\">.</span><span class=\"token function\">getLogger</span><span class=\"token punctuation\">(</span>\n        <span class=\"token class-name\">LoggingEventHandler</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">class</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        logger<span class=\"token punctuation\">.</span><span class=\"token function\">info</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Event occurred: {} for aggregate: {} at version: {}\"</span><span class=\"token punctuation\">,</span>\n            event<span class=\"token punctuation\">.</span><span class=\"token function\">getEventType</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            event<span class=\"token punctuation\">.</span><span class=\"token function\">getAggregateId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            event<span class=\"token punctuation\">.</span><span class=\"token function\">getVersion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"9-2-메트릭-수집\" style=\"position:relative;\"><a href=\"#9-2-%EB%A9%94%ED%8A%B8%EB%A6%AD-%EC%88%98%EC%A7%91\" aria-label=\"9 2 메트릭 수집 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>9-2. 메트릭 수집</h3>\n<div class=\"gatsby-highlight\" data-language=\"java\"><pre class=\"language-java\"><code class=\"language-java\"><span class=\"token comment\">// MetricsEventHandler.java</span>\n<span class=\"token annotation punctuation\">@Component</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">MetricsEventHandler</span> <span class=\"token punctuation\">{</span>\n    \n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">final</span> <span class=\"token class-name\">Counter</span> eventCounter<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">final</span> <span class=\"token class-name\">Timer</span> eventProcessingTime<span class=\"token punctuation\">;</span>\n    \n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">MetricsEventHandler</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">MeterRegistry</span> meterRegistry<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>eventCounter <span class=\"token operator\">=</span> <span class=\"token class-name\">Counter</span><span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"events.total\"</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">description</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Total number of events\"</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">register</span><span class=\"token punctuation\">(</span>meterRegistry<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        \n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>eventProcessingTime <span class=\"token operator\">=</span> <span class=\"token class-name\">Timer</span><span class=\"token punctuation\">.</span><span class=\"token function\">builder</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"events.processing.time\"</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">description</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Event processing time\"</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token function\">register</span><span class=\"token punctuation\">(</span>meterRegistry<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    \n    <span class=\"token annotation punctuation\">@EventListener</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">void</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">BaseEvent</span> event<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        eventCounter<span class=\"token punctuation\">.</span><span class=\"token function\">increment</span><span class=\"token punctuation\">(</span>\n            <span class=\"token class-name\">Tags</span><span class=\"token punctuation\">.</span><span class=\"token function\">of</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"type\"</span><span class=\"token punctuation\">,</span> event<span class=\"token punctuation\">.</span><span class=\"token function\">getEventType</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"10-결론\" style=\"position:relative;\"><a href=\"#10-%EA%B2%B0%EB%A1%A0\" aria-label=\"10 결론 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>10. 결론</h2>\n<p>Event Sourcing과 CQRS는 복잡하지만 강력한 아키텍처 패턴이다. 이 글에서 다룬 내용:</p>\n<ol>\n<li><strong>기본 개념</strong>: Event Sourcing, CQRS 패턴</li>\n<li><strong>이벤트 정의</strong>: 도메인 이벤트 설계</li>\n<li><strong>Aggregate Root</strong>: 이벤트 기반 도메인 모델</li>\n<li><strong>이벤트 스토어</strong>: 이벤트 저장 및 조회</li>\n<li><strong>CQRS 구현</strong>: 명령/쿼리 분리</li>\n<li><strong>스냅샷</strong>: 성능 최적화</li>\n<li><strong>이벤트 발행/구독</strong>: 이벤트 기반 통신</li>\n<li><strong>Spring Boot 통합</strong>: 실전 구현</li>\n<li><strong>실전 패턴</strong>: 버전 관리, 순서 보장, 재처리</li>\n</ol>\n<p>이러한 패턴들을 적절히 활용하면 확장 가능하고 유연한 이벤트 기반 시스템을 구축할 수 있다.</p>\n<h2 id=\"참고-자료\" style=\"position:relative;\"><a href=\"#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C\" aria-label=\"참고 자료 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>참고 자료</h2>\n<ul>\n<li><a href=\"https://martinfowler.com/eaaDev/EventSourcing.html\">Event Sourcing 패턴</a></li>\n<li><a href=\"https://martinfowler.com/bliki/CQRS.html\">CQRS 패턴</a></li>\n<li><a href=\"https://axoniq.io/\">Axon Framework</a></li>\n<li><a href=\"https://www.eventstore.com/\">EventStore</a></li>\n</ul>","tableOfContents":"<ul>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#event-sourcing%EA%B3%BC-cqrs-%ED%8C%A8%ED%84%B4---%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C\">Event Sourcing과 CQRS 패턴 - 이벤트 기반 아키텍처 설계 실전 가이드</a></p>\n<ul>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#1-event-sourcing%EA%B3%BC-cqrs-%EA%B0%9C%EC%9A%94\">1. Event Sourcing과 CQRS 개요</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#1-1-event-sourcing%EC%9D%B4%EB%9E%80\">1-1. Event Sourcing이란?</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#1-2-cqrs%EB%9E%80\">1-2. CQRS란?</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#1-3-%EC%A0%84%ED%86%B5%EC%A0%81-%EB%B0%A9%EC%8B%9D-vs-event-sourcing--cqrs\">1-3. 전통적 방식 vs Event Sourcing + CQRS</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#2-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98\">2. 기본 개념과 아키텍처</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#2-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%95%EC%9D%98\">2-1. 이벤트 정의</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#2-2-aggregate-root\">2-2. Aggregate Root</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#3-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%8A%A4%ED%86%A0%EC%96%B4-%EA%B5%AC%ED%98%84\">3. 이벤트 스토어 구현</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#3-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%8A%A4%ED%86%A0%EC%96%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4\">3-1. 이벤트 스토어 인터페이스</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#3-2-jpa-%EA%B8%B0%EB%B0%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%8A%A4%ED%86%A0%EC%96%B4\">3-2. JPA 기반 이벤트 스토어</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#3-3-repository-%ED%8C%A8%ED%84%B4\">3-3. Repository 패턴</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-cqrs-%EA%B5%AC%ED%98%84\">4. CQRS 구현</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-1-%EB%AA%85%EB%A0%B9-%EC%B2%98%EB%A6%AC-write-side\">4-1. 명령 처리 (Write Side)</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-2-%EC%9D%BD%EA%B8%B0-%EB%AA%A8%EB%8D%B8-read-side\">4-2. 읽기 모델 (Read Side)</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-3-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%95%B8%EB%93%A4%EB%9F%AC-%EC%9D%BD%EA%B8%B0-%EB%AA%A8%EB%8D%B8-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8\">4-3. 이벤트 핸들러 (읽기 모델 업데이트)</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-4-%EC%BF%BC%EB%A6%AC-%EC%B2%98%EB%A6%AC-read-side\">4-4. 쿼리 처리 (Read Side)</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#5-%EC%8A%A4%EB%83%85%EC%83%B7-%ED%8C%A8%ED%84%B4\">5. 스냅샷 패턴</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#5-1-%EC%8A%A4%EB%83%85%EC%83%B7-%EA%B5%AC%ED%98%84\">5-1. 스냅샷 구현</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#6-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%9C%ED%96%89%EA%B3%BC-%EA%B5%AC%EB%8F%85\">6. 이벤트 발행과 구독</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#6-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%9C%ED%96%89%EC%9E%90\">6-1. 이벤트 발행자</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#6-2-%EC%99%B8%EB%B6%80-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%9C%ED%96%89-kafka\">6-2. 외부 시스템 이벤트 발행 (Kafka)</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#6-3-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B5%AC%EB%8F%85%EC%9E%90\">6-3. 이벤트 구독자</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#7-spring-boot-%ED%86%B5%ED%95%A9\">7. Spring Boot 통합</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#7-1-%EC%84%A4%EC%A0%95\">7-1. 설정</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#7-2-rest-api\">7-2. REST API</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#8-%EC%8B%A4%EC%A0%84-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EB%AA%A8%EB%B2%94-%EC%82%AC%EB%A1%80\">8. 실전 패턴과 모범 사례</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#8-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC\">8-1. 이벤트 버전 관리</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#8-2-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%88%9C%EC%84%9C-%EB%B3%B4%EC%9E%A5\">8-2. 이벤트 순서 보장</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#8-3-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%9E%AC%EC%B2%98%EB%A6%AC\">8-3. 이벤트 재처리</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#9-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81%EA%B3%BC-%EB%94%94%EB%B2%84%EA%B9%85\">9. 모니터링과 디버깅</a></p>\n<ul>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#9-1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A1%9C%EA%B9%85\">9-1. 이벤트 로깅</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#9-2-%EB%A9%94%ED%8A%B8%EB%A6%AD-%EC%88%98%EC%A7%91\">9-2. 메트릭 수집</a></li>\n</ul>\n</li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#10-%EA%B2%B0%EB%A1%A0\">10. 결론</a></li>\n<li><a href=\"/springboot/Event-Sourcing%EA%B3%BC-CQRS-%ED%8C%A8%ED%84%B4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C\">참고 자료</a></li>\n</ul>\n</li>\n</ul>","wordCount":{"words":321},"fields":{"slug":"/springboot/Event-Sourcing과-CQRS-패턴-이벤트-기반-아키텍처-설계-실전-가이드/","image":null,"faq":[]},"frontmatter":{"title":"Event Sourcing과 CQRS 패턴 - 이벤트 기반 아키텍처 설계 실전 가이드","date":"2025년 12월 7일","dateISO":"2025-12-07T00:00:00.000Z","updatedISO":null,"category":"springboot","description":"Event Sourcing과 CQRS 패턴의 기초부터 실전 구현까지. 이벤트 스토어 설계, 이벤트 리플레이, 스냅샷, 읽기 모델 동기화, Spring Boot 구현, 실전 예제까지 완벽하게 다룹니다.","tags":["Event Sourcing","CQRS","이벤트 기반 아키텍처","Spring Boot","이벤트 스토어","도메인 이벤트","마이크로서비스","분산 시스템"]}}},"pageContext":{"slug":"/springboot/Event-Sourcing과-CQRS-패턴-이벤트-기반-아키텍처-설계-실전-가이드/","previous":{"fields":{"slug":"/ai/AI-3D-생성-혁명-텍스트에서-3D-모델을-만드는-시대/"},"frontmatter":{"title":"AI 3D 생성 혁명 텍스트에서 3D 모델을 만드는 시대","tags":["AI 3D 생성","3D 모델 생성","Text-to-3D","3D AI","생성형 AI","AI","3D 모델링"]}},"next":{"fields":{"slug":"/javascript/웹-게임-개발-기술-스택-완전정복-실전-가이드/"},"frontmatter":{"title":"웹 게임 개발 기술 스택 완전정복 - 실전 가이드","tags":["웹 게임","게임 개발","Phaser","Three.js","Babylon.js","Unity WebGL","Canvas API","WebGL","게임 엔진","JavaScript","HTML5 게임"]}}}},"staticQueryHashes":["213619243","3262363727"]}