Post

분산 트랜잭션 패턴

실무에서 서비스에서 분산 트랜잭션을 사용해야할 때 분산 트랜잭션의 종류와 개념에 대해 설명합니다.

분산 시스템의 문제

실무에서는 서비스가 여러 데이터베이스 서버를 업데이트하거나 삽입해야하는 경우가 있습니다. 서비스의 비즈니스 로직에서 A,B,C 3개의 데이터베이스가 작업이 이루어질 때 A,B 모두 삽입이 이루어지고 C가 에러가 발생하고 롤백되지 않는다면, 시스템 영구적으로 비일관적인 상태에 빠지게 되어 데이터 정합성이 깨지고 비즈니스 로직에 심각한 오류를 초래할 수 있습니다.

이러한 문제로 3개의 데이터베이스가 모두 성공하거나 모두 실패하는 것을 보장하는 메커니즘을 필요로 하며, Spring에서 분산 트랜잭션 관리 패턴을 전략적으로 어떤것을 사용할 수 있는지 정리해보도록 하겠습니다.

아키텍처 제약 조건으로서의 CAP 이론

이러한 분산 트랜잭션 문제를 이해하는데 있어 CAP이론은 핵심적인 이론적 틀을 제공합니다. CAP이론은 분산 컴퓨팅 시스템의 일관성(Consistency), 가용성(Availability), 분할 감내(Partition Tolerance)라는 세가지 보장 중 동시에 두 가지만 만족시킬 수 있다는 원칙입니다. 네트워크 분할은 현대 분산 시스템에서 피할 수 없는 현실이므로, 설계자는 일관성과 가용성 사이에서 트레이드오프를 해야합니다.

강력한 일관성을 선택하면(예: 모든 노드가 동일한 데이터를 보장), 네트워크 분할 시 일부 노드가 응답하지 못하게되어 가용성이 저하될 수 있습니다. 반면, 높은 가용성을 보장하면(예: 모든 요청이 항상 응답 받도록 보장), 네트워크 분할 시 일부 노드가 최신이 아닌 데이터를 반환하여 일시적으로 일관성이 깨질 수 있습니다. 어떤 트랜잭션 관리 패턴을 선택할 것인지는 이 CAP이론의 제약 조건 하에서 비즈니스 요구사항에 따라 어떤 특성을 우선시할 것인지에 대한 아키텍처 결정과 직결됩니다.

이중 쓰기 문제(The Dual-Write Problem)

하나의 서비스가 단일 작업의 일부로 자신의 데이터베이스를 쓰고 동시에 외부 시스템(예: 메시지 브로커)에 메시지를 발행하려고 할 때 발생합니다. 예를 들어, 주문 서비스가 데이터베이스에 주문 정보 커밋한 직후, 주문 생성 이벤트를 kafka에 발행하기 전에 시스템 장애가 발생했다고 가정해 봅시다. 데이터베이스 트랜잭션은 이미 성공적으로 커밋되었지만, 이벤트는 발행되지 않았습니다. 결과적으로 재고 서비스나 알림 서비스와 같은 다른 서비스들은 새로운 주문 생성되었다는 사실을 전혀 알지 못하게 되며, 시스템 전체적으로는 데이터 불일치가 발생하게 됩니다. 이 문제는 감지하기 어렵고 조용한 데이터 손실로 이루어질 수 있기 때문에 매우 위험합니다. 이 이중 쓰기 문제를 해결하는 것이 바로 트랜잭셔널 아웃박스(Transactional Outbox)와 같은 패턴의 핵심 동기입니다.

JTA/XA 패턴

원자성을 보장하기 위한 고전적이고 표준적인 해결책은 2단계 커밋(Two-Phase Commit, 2PC) 프로토콜입니다. 이 프로토콜은 여러 분산된 리소스가 모두 동의할 때만 트랜잭션을 커밋되는 ‘전부 아니면 전무(all-or-nothing)’를 보장합니다.

X/Open 그룹이 정의한 XA(eXtended Architecture)은 2단계 커밋 프로토콜 산업의 표준입니다. XA는 서로 다른 벤터의 데이터베이스나 메시지 큐와 같은 이기종 리소스 매니저들이 단일 전역 트랜잭션(global trasaction)에 참여할 수 있도록 하는 표준 인터페이스를 정의합니다.

Java 환경에서는 이러한 XA 표준을 애플리케이션 수준에서 다룰 수 있도록 Java Transaction API(JTA)라는 표준 API 제공 합니다. JTA는 개발자가 트랜잭션 관리자(Transaction Manager)와 상호작용하여 분산 트랜잭션을 시작, 커밋, 롤백할 수 있는 일관된 방법을 제공하며, @Transactional 어노테이션과 같은 선언적 트랜잭션 모델을 통해 복잡한 분산 트랜잭션 로직을 추상화 합니다.

프로토콜 구성 요소 및 역할

  • 트랜잭션 관리자(Transaction Manager, TM) / 코디네이터(Coordinator)

    분산 트랜잭션의 전체 조율을 담당하는 중앙 제어 장치입니다. TM은 트랜잭션을 시작하고, 모든 참여자에게 트랜잭션 준비를 요청하며, 모든 참여자의 응답을 바탕으로 최종 커밋 또는 중단결정을 내립니다.

    Spring Boot환경에서는 Atomikos, Narayana, Bitronix와 같은 내장 JTA 제공자가 이 역할을 수행합니다.

  • 리소스 관리자(Resource Manager, RM) / 참여자(Participant)

    트랜잭션에 참여하는 개별 리소스를 의미합니다. 이는 데이터베이스, 메시지 큐 등 XA 프로토콜을 지원하는 모든 시스템이 될 수 있습니다. 각 RM은 자신의 로컬 트랜잭션을 관리하며, TM의 지시에 따라 준비, 커밋, 롤백을 수행합니다. 분산 시스템의 문제점 시나리오에서 데이터베이스 A, B, C는 각각의 리소스 관리자 역할을 합니다.

2PC 상세 흐름

2PC 프로토콜은 트랜잭션 커밋 과정은 두개의 단계로 나뉩니다.

  • 1단계: 준비 단계(Prepare Phase / Voting Phase)
    1. TM은 전역 트랜잭션에 참여하는 모든 RM에게 prepare 명령어를 보냅니다.
    2. prepare 명령을 받은 각 RM은 자신의 로컬 트랜잭션을 커밋할 수 있는지 확인합니다. 이 과정에서 필요한 모든 데이터베이스 잠금(lock)을 획득하고, 변경사항을 디스크의 트랜잭션 로그(Write-Ahead Log, WAL)에 영구적으로 기록합니다. 이 시스템이 중간에 다운되더라도 복구 후 트랜잭션을 커밋하거나 롤백할 수 있도록 보장하기 위함입니다.
    3. RM은 로컬 트랜잭션을 성공적으로 커밋할 수 있다고 판단되면 TM에게 ‘Yes’ 투표(Vote)를 보냅니다. 만약 어떤 이유로든 커밋이 불가능하다고 판단되면(예: 제약 조건 위반, 잠금 획득 실패), ‘No’투표를 보냅니다.
    4. ‘Yes’투표는 구속력있는 약속입니다. ‘Yes’를 보낸 RM은 TM으로부터 최종 commit 또는 rollback 명령어이 올 때까지 해당 트랜잭션과 모든 리소스를 유지하고 대기해야합니다.
  • 2단계: 커밋 단계(Commit Phase / Decision Phase)
    1. TM은 모든 RM으로부터 투표를 수집합니다.
    2. 만약 모든 RM이 ‘Yes’로 투표했다면, TM은 자신의 로그에 ‘commit’결정을 기록하고 모든 RM에게 commit 명령을 보냅니다. commit 명령을 받은 각 RM은 자신의 로컬 트랜잭션을 최종적으로 커밋하고, 보유하고 있던 잠금을 해제합니다.
    3. 만약 단 하나라도 RM이 ‘No’로 투표했거나, 정해진 시간 내에 응답하지 않았다면, TM은 자신의 로그에 ‘rollback’결정을 기록하고 모든 RM에게 rollback 명령을 보냅니다. rollback 명령을 받은 RM은 트랜잭션 로그를 사용하여 변경 사항을 모두 취소하고 잠금을 해제합니다.

장애 분석 및 복구

2PC는 강력한 일관성을 제공하지만, 여러 장애 시나리오에 취약점을 가지고 있습니다.

  • TM 장애

    2PC의 가장 큰 약점입니다. 만약 TM이 1단계에서 모든 RM에게 prepare 명령어를 보낸후, 2단계 최종 commit 또는 rollback 결정을 전파하기 전에 장애가 발생한다면, 모든 RM들은 불확실한상태에 빠지게 됩니다. RM들은 트랜잭션을 커밋해야할지 롤백해야할지 알 수 없기 때문에, 리소스에 대한 잠금을 무기한으로 유지하며 TM이 복구되기를 기다려야 합니다. 이는 해당 리소스를 필요로 하는 다른 모든 프로세스를 차단하여 시스템 전체의 가용성을 심하게 저하시킬 수 있습니다.

  • RM 장애

    RM이 준비 단계에서 장애를 일으키면, TM은 타임아웃으로 이를 감지하고 전체 트랜잭션을 롤백시킵니다. RM이 커밋 단계에서 장애를 일으킬 경우, TM은 복구 프로세스를 통해 주기적으로 해당 RM에게 재연결하여 최종 결정이(커밋 또는 롤백)이 반드시 실행되도록 보장됩니다.

  • 로그의 역할

    2PC 프로토콜의 지속성과 복구 능력은 전적으로 TM과 RM들의 안정적인 쓰기 전용 로그(WAL)에 의존합니다. 장애 발생 시, 각 구성 요소는 자신의 로그를 참조하여 장애 발생 진적의 상태를 복원하고 트랜잭션을 일관된 상태로 완료할 수 있습니다.

정리

2PC 프로토콜은 모든 RM이 동기적으로 통신하고 응답해야하므로 네트워크 지연 시간에 민감하며, 트랜잭션이 진행되는 동안 리소스를 잠그기 때문에 시스템 처리량을 감소시킬 수 있습니다. 따라서 2PC는 완벽한 데이터 일관성이 비즈니스적으로 매우 중요하고, 트랜잭션 볼륨이 비교적 낮으며, 참여하는 시스템들의 가용성이 높은 환경에 적합합니다. 반면, 높은 처리량과 가용성이 요구되는 대규모 마이크로서비스 환경에서는 2PC의 블로킹 특성과 TM 장애에 대한 취약점이 큰 단점으로 작용할 수 있습니다.

JTA/TA 예제 소스

현대적인 분산 트랜잭션 패턴

2PC가 제공하는 강력한 일관성은 매력적이지만, 그에 따르는 성능 및 가용성 비용은 대규모 분산 시스템, 특히 마이크로 서비스 아키텍처(MSA)에서는 감당하기 어려운 경우가 많습니다. 이에 따라 업계에서는 즉각적인 강력한 일관성 대신, 시간이 지남에 따라 데이터가 일관된 상태에 도달하는 최종 일관성(Eventual Consistency)을 목표로 하는 대안적인 패턴들을 발전시켜왔습니다. 이 패턴들은 시스템의 가용성과 확장성을 우선시하며, 서비스간의 결합도를 낮추는 데 중점을 둡니다.

Saga 패턴

Saga 패턴은 단일의 원자적 트랜잭션으로 처리하기 어려운 장기 실행 비즈니스 프로세스를 일련의 독립적인 로컬 트랜잭션들로 분해하여 관리하는 기법입니다. 각 로컬 트랜잭션은 하나의 서비스 내에서 원자적으로 실행되며, 성공적으로 완료되면 다음 단계를 트리거하는 메시지나 이벤트를 발행합니다.

핵심 원칙

Saga의 핵심은 실패 처리 방식에 있습니다. 만약 Saga의 여러 단계 중 어느 한 단계에서 로컬 트랜잭션이 실패하면, Saga는 이미 성공적으로 완료된 이전 단계들의 작업을 되돌리는 일련의 보상 트랜잭션(Compensation Transactions)을 역순으로 실행합니다. 예를 들어, 주문 생성(성공) → 결제 처리(성공) → 재고 차감(실패)의 시나리오에서, Saga는 ‘재고 차감 실패’에 대한 응답으로 ‘결제 취소’와 ‘주문 취소’라는 보상 트랜잭션을 실행 합니다. 이를 통해 전체 비즈니스 프로세스는 결국 원자적으로 실행된 것과 같은 의미적 일관성을 갖게 됩니다. 즉, ‘의미론적 원자성(semantic atomicity)’을 달성하는 것입니다.

구현 모델

Saga는 주로 두 가지 방식으로 구현됩니다: 코레오그래피(Choreography)와 오케스트레이션(Orchestration)

  • 코레오그래피(Choreography, 이벤트 기반)

    코레오그래피 모델은 중앙 조정자 없이, 각 서비스가 독립적으로 이벤트를 발행하고 구독하며 상호작용하는 분산형 방식입니다. 각 서비스는 자신의 로컬 트랜잭션을 완료한 후, ‘주문이 생성됨(OrderCreated)’과 같은 도메인 이벤트를 메시지 브로커에 발행합니다. 그러면 해당 이벤트를 구독하고 있는 다른 서비스(예: 결제 서비스, 재고 서비스)가 자신의 로컬 트랜잭션을 수행하고, 또 다른 이벤트를 발생하는 연쇄적인 방식으로 Saga가 진행됩니다.

    • 장점

      서비스 간의 결합도가 매우 낮습니다. 각 서비스의 어떤 이벤트에 반응하고 어떤 이벤트를 발행할지만 알면 되므로, 다른 서비스의 존재나 구현에 대해 알 필요가 없습니다. 또한 중앙 조정자가 없으므로 단일 장애점(Single Point of Failure)이 없어 시스템의 회복탄련성이 높고, 각 팀이 자율적으로 서비스를 개발하고 배포하기에 용이합니다.

    • 단점

      전체 비즈니스 워크플로우를 한눈에 파악하기 어렵습니다. 트랜잭션의 현재 상태를 추적하거나 디버깅하는 것이 매우 복잡해집니다(’내 트랜젹션은 지금 어디에 있는가?’). 서비스 간의 이벤트 구독 관계가 복잡해지면 순환 종속성이 발생할 위험도 있습니다.

      예제 소스

  • 오케스트레이션(Orchestration, 커맨드 기반)

    오케스트레이션 모델은 ‘사가 오케스트레이터(Saga Orchestrator)’ 또는 ‘사가 실행 코디네이터(Saga Execution Coordinator, SEC)’라고 불리는 중앙 집중형 컴포넌트가 전체 워크플로우를 관리하는 방식입니다. 오케스트레이터는 비즈니스 프로세스의 모든 단계를 알고 있으며, 각 서비스에게 ‘결제를 처리하라’ 또는 ‘재고를 차감하라’와 같은 명시적인 명령(command)을 보냅니다. 서비스는 명령을 수행한후 결과를 오케스트레이터에게 다시 보고하고, 오케스트레이터는 그 결과를 바탕으로 다음 단계를 진행할지, 아니면 보상 트랜잭션을 실행할지를 결정합니다.

    • 장점

      전체 워크플로우 로직이 오케스트레이터 한 곳에 중앙 집중화되어 있어 프로세스를 이해하고, 관리하며, 디버깅하기가 훨씬 쉽습니다. 상태 관리가 명시적이므로 오류 처리와 보상 로직을 구현하기가 더 간단합니다.

    • 단점

      오케스트레이터가 단일 장애점이 될 수 있습니다. 만약 오케스트레이터가 다운되면 관련된 모든 트랜잭션이 중단됩니다. 또한, 참여하는 서비스들이 오케스트레이터의 API나 명령 형식에 종속되어 코레오그래피 방식에 비해 결합도가 높아집니다.

      예제 소스

장애 분석

Saga 패턴은 강력한 대안이지만, 몇 가지 중요한 도전 과제를 안고 있습니다.

  • ACID 격리성(Isolation)의 부재

    이는 Saga 패턴을 채택할 때 발생하는 가장 중요한 트레이드오프입니다. Saga의 각 단계는 독립적인 로컬 트랜잭션으로 즉시 커밋되기 때문에, 해당 변경 사항은 전체 Saga가 완료되기 전에 다른 트랜잭션에 노출될 수 있습니다.

    이는 ‘더티 리드(dirty read)’와 같은 데이터 이상 현상을 유발할 수 있습니다. 예를 들어, 재고 서비스가 재고를 차감했지만 나중에 결제가 실패하여 Saga가 롤백되는 동안, 다른 사용자가 일시적으로 줄어든 재고량을 보고 상품이 품절되었다고 오인할 수 있습니다. 이를 온화하기 위해 ‘의미론적 잠금(semantic locking)’과 같은 대책이 필요합니다. 예를 들어, 주문 상태를 CREATED 가 아닌 PAYMENT_PENDING 과 같은 중간 상태로 설정하여 다른 프로세스가 해당 주문에 대해 작업을 수행하지 못하도록 막는 방식입니다.

  • 멱등성(Idempotency)과 교환 법칙(Commutativity)

    보상 트랜잭션은 반드시 멱등성을 가져야 합니다. 즉, 네트워크 오류 등으로 인해 동일한 보상 요청이 여러 번 전달되더라도 시스템 상태가 한 번만 적용된 것과 동일하게 유지되어야 합니다. 또한, 보상 트랜잭션은 실패할 수 없습니다. 실패할 경우, 성공할 때까지 무기한 재시도되어야 하며, 경우에 따라서는 운영자의 수동 개입이 필요할 수도 있습니다. 이는 보상 로직의 설계와 구현에 상당한 부담을 줍니다.

정리

Saga 패턴은 2PC와 같은 인프라 수준의 기술적 원자성 보장에서 애플리케이션 로직 수준의 의미론적 원자성 보장으로의 근본적인 사고의 전환을 요구합니다. 일관성 유지의 책임이 트랜잭션 관리자에서 개발자에게로 이동하는 것입니다. 이는 단순히 기술을 선택하는 문제를 넘어, 최종 일관성을 수용하고 이에 따른 복잡성을 도메인 모델링에 명시적으로 반영하겠다는 아키텍처적 결정을 의미합니다.

아웃박스 패턴

코레오그래피 방식의 Saga는 서비스 간의 이벤트 통신에 크게 의존합니다. 그러나 여기서 앞서 언급한 ‘이중 쓰기 문제’가 다시 발생합니다. 서비스가 자신의 데이터베이스 상태를 원자적으로 업데이트하면서 동시에 다른 서비스가 소비할 이벤트를 신뢰성 있게 발행하려면 트랜잭셔널 아웃박스 패턴을 사용해야합니다.

상세 흐름

아웃박스 패턴의 핵심 아이디어는 두 개의 쓰기 작업(비즈니스 데이터 저장, 이벤트 발행)을 동일한 데이터베이스 내에서 하나의 트랜잭션으로 묶는 것입니다.

  1. 서비스는 비즈니스 로직을 수행하면서 자신의 주 데이터 테이블(예: ORDERS 테이블)에 데이터를 삽입하거나 업데이트 합니다.
  2. 동일한 데이터베이스 트랜잭션 내에서, 발행해야 할 이벤트에 대한 정보를 별도의 OUTBOX 테이블에 삽입합니다. 이 OUTBOX 테이블 레코드에는 이벤트 유형, 페이로드, 대상 토픽 등의 정보가 포함됩니다.
  3. 이 두 작업이 하나의 트랜잭션으로 묶여 있기 때문에, 데이터베이스는 둘다 성공적으로 커밋되거나 둘다 롤백되는 것을 보장합니다. 이로써 데이터베이스 상태 변경과 이벤트 발행 의도가 원자적으로 연결됩니다.
  4. ‘메시지 릴레이(Message Relay)’라고 불리는 별도의 비동기 프로세스가 이 OUTBOX 테이블을 주기적으로 확인합니다.
  5. 메시지 릴레이는 OUTBOX 테이블에서 아직 처리되지 않은 이벤트를 읽어와 실제 메시지 브로커로 발행합니다.
  6. 메시지가 성공적으로 발행되면, 릴레이는 해당 이벤트를 OUTBOX 테이블에서 삭제하거나 ‘처리 완료’ 상태로 표시하여 중복 발행을 방지합니다.

메시지 릴레이 구현 전략

메시지 릴레이를 구현하는 방식은 크게 두 가지로 나뉩니다.

  • 폴링 퍼블리셔(Polling Publisher)

    가장 간단한 접근 방식으로, 릴레이 프로세스가 정해진 간격(예: 매초)으로 OUTBOX 테이블에 새로운 레코드가 있는지 쿼리하는 방식입니다. 구현이 비교적 쉽지만, 폴링 간격에 따라 이벤트 발행에 지연이 발생할 수 있으며, 잦은 폴링은 데이터베이스에 불필요한 부하를 줄 수 있습니다.

  • 로그 기반 변경 데이터 캡처(Change Data Capture, CDC)

    이 방식에서는 릴레이 프로세스가 데이터베이스의 트랜잭션 로그(예: PostgresSQL의 WAL, MySQL의 binlog)를 직접 읽습니다. Debezium과 같은 도구를 사용하여 OUTBOX 테이블에 대한 커밋된 변경 사항을 거의 실시간으로 감지하고 이를 이벤트로 변환하여 메시지 브로커로 전달합니다. 이 방식은 폴링에 비해 지연 시간이 매우 짧고 데이터베이스에 미치는 부하가 거의 없습니다.

정리

아웃박스 패턴은 Saga 패턴의 대안이 아니라, 특히 코레오그래피 모델을 위한 매우 중요한 활성화 패턴(enabling pattern)입니다. 코레오그래피 Saga는 신뢰할 수 있는 이벤트 발행이라는 기본 전제 위에서 동작하는데, 아웃박스 패턴이 바로 그 전체를 기술적으로 보장해주는 역할을 합니다. 즉, 상태 기반의 영속성을 사용하는 시스템에서 견고한 코레오그래피 Saga를 구현하기 위해서는 아웃박스 패턴의 적용이 거의 필수적이라고 할 수 있습니다.

이벤트 소싱

엔티티의 현재 상태를 저장하는 대신, 해당 엔티티의 상태를 변경 시킨 모든 이벤트들의 전체 이력을 시간 순서대로 불변의 추가 적용 로그(append-only log)에 저장하는 방식입니다. 엔티티의 현재 상태는 필요할 때마다 이 이벤트들을 처음부터 순서대로 재실행하여 계산합니다. 예를 들어, 고객 객체의 상태를 name: "John", address: "123 Main St” 같이 저장하는 대신, CustomerCreated(name: "John")CustomerAddressChanged(address: "123 Main St") 와 같은 이벤트 시퀀스를 저장하는 것입니다.

이벤트 소싱이 이중 쓰기 문제를 근본적으로 해결하는 방법

이벤트 소싱 아키텍처에서 이벤트 저장소(Event Store)는 시스템의 유일한 진실의 원천(Single Source of Truth)이 됩니다. 상태를 변경하는 유일한 방법은 새로운 이벤트를 이벤트 저장소에 추가하는 것이며, 이 ‘이벤트 저장’ 행위 자체가 원자적 작업입니다. 여기서 중요한 점은, 이벤트 저장소 자체가 메시지 브로커의 역할을 겸할 수 있다는 것입니다. 이벤트가 저장소에 성공적으로 추가되면, 저장소는 이 이벤트를 구독하고 있는 다른 서비스들에게 발행할 수 있습니다. 즉, ‘데이터베이스에 쓰기’와 ‘메시지 발행’이 사실상 하나의 동일한 작업이 되는 것입니다. 동기화해야 할 ‘두 번째 쓰기’가 존재하지 않으므로, 이중 쓰기 문제는 근복적으로 사라집니다.

CQRS와의 공생 관계

이벤트 소싱은 쓰기 작업에 매우 최적화되어 있지만, 쿼리 작업에는 비효율적일 수 있습니다. 특정 엔티티의 현재 상태를 알기 위해 매번 수 많은 이벤트를 재실행하는 것은 성능에 부담이 될 수 있기 때문입니다. 이러한 단점을 보완하기 위해 이벤트 소싱은 명령 조회 책임 분리(Command Query Responsibility Segregation, CQRS) 패턴과 함께 사용됩니다.

CQRS는 시스템의 상태를 변경하는 명령(Command)을 처리하는 책임과, 시스템의 상태를 조회(Query)하는 책임을 명확히 분리하는 패턴입니다. 이벤트 소싱과 결합될 때, 다음과 같은 구조가 됩니다.

  • 쓰기 모델(Write Model)

    이벤트 저장소가 이 역할을 담당합니다. 모든 명령은 새로운 이벤트를 생성하여 이벤트 저장소에 추가하는 것으로 처리 됩니다.

  • 읽기 모델(Read Model)

    쿼리에 최적화된 별도의 데이터 저장소 입니다. 이 읽기 모델은 이벤트 저장소에서 발행되는 이벤트 스트림을 구독하여 자신의 상태를 비동기적으로 업데이트합니다. 예를 들어, RDB, NoSQL, 검색 엔진 등 쿼리 요구사항에 가장 적합한 기술을 사용하여 다양한 형태의 ‘프로젝션’을 만들 수 있습니다.

정리

이벤트 소싱은 시스템이 데이터를 모델링하고 영속화하는 방식을 근복적으로 바꾸는 아키텍처 패턴입니다. 이벤트 발행 문제를 우아하게 해결하는 동시에, 시스템의 모든 변경 이력에 대한 완벽한 감사 로그 제공하는 강력한 이점을 가집니다. 그러나 이벤트 스키마의 진화, 읽기 모델 프로젝션의 구축 및 유지 관리, 과거 상태 재구성의 복잡성 등 새로운 차원의 과제들을 동반합니다. 따라서 이벤트 소싱을 채택하는 것은 높은 복잡성을 감수하고 높은 보상을 얻으려는 전략적 결정이며, 이벤트 중심적 사고방식과 관련 패턴들에 대한 깊은 이해를 필요로 합니다.

This post is licensed under CC BY 4.0 by the author.