104 lines
9.7 KiB
Markdown
104 lines
9.7 KiB
Markdown
---
|
|
title: Transactional Outbox Pattern — microservices.io (Chris Richardson)
|
|
source_type: official-doc
|
|
url: https://microservices.io/patterns/data/transactional-outbox.html
|
|
archive_url:
|
|
status: raw
|
|
confidence: medium
|
|
tags: [architecture, transactional-outbox, dual-write, eventual-consistency, messaging, ca-skeleton-operational-contract]
|
|
related_projects: [ca-skeleton-operational-contract]
|
|
related_branches: [feature-repository-access-permission-contract, feature-domain-event-outbox-contract]
|
|
created: 2026-05-27
|
|
last_reviewed: 2026-05-27
|
|
---
|
|
|
|
# Transactional Outbox Pattern — microservices.io (Chris Richardson)
|
|
|
|
> Layer: `raw/official-docs/` — Chris Richardson 의 microservices.io 패턴 카탈로그 중 "Transactional Outbox" 페이지 verbatim 발췌. dual-write 문제와 OUTBOX 테이블 기반 해결책의 1차 인용 출처.
|
|
|
|
## Parent / 활용 branch (필수)
|
|
|
|
| Branch | 이 자료가 정당화하는 결정 |
|
|
|---|---|
|
|
| [[raw/branch-notes/feature-repository-access-permission-contract]] | D7 (Repository 가 도메인 이벤트 발행 책임을 가질지, 아니면 outbox 테이블 write 만 책임지고 별도 relay 가 발행할지) 결정의 근거 |
|
|
| [[raw/branch-notes/feature-domain-event-outbox-contract]] | OUTBOX 테이블 + 별도 message relay 채택의 1차 근거 — "dual write 문제" 정의와 단일 local transaction 해결책 |
|
|
|
|
## 컨텍스트
|
|
|
|
ca-tmpl 이 도메인 이벤트를 어떻게 외부 메시지 브로커로 안전하게 전달할지의 청사진을 결정해야 한다. 가장 흔한 함정인 "DB commit 후 메시지 발행 실패" 또는 "메시지 발행 후 DB rollback" 의 inconsistency 를 방지하기 위한 표준 패턴이 transactional outbox. 본 raw 는 패턴 정의와 force/result 의 1차 출처.
|
|
|
|
## 출처 / Source
|
|
|
|
- 원본 URL: https://microservices.io/patterns/data/transactional-outbox.html
|
|
- 아카이브 URL:
|
|
- 저자 / 조직: Chris Richardson — microservices.io (personal pattern catalog). 별도 vendor 의 공식 문서 아님.
|
|
- 발행일: rolling docs (페이지에 "Copyright © 2026" 표기, 최초 작성 시점 명시는 없음)
|
|
- 마지막 확인일: 2026-05-27
|
|
|
|
## 핵심 인용 / Key quotes (verbatim)
|
|
|
|
> [§Context] "A service command typically needs to create/update/delete aggregates in the database **and** send messages/events to a message broker."
|
|
|
|
> [§Context] "The command must atomically update the database and send messages in order to avoid data inconsistencies and bugs."
|
|
|
|
> [§Problem] "How to atomically update the database and send messages to a message broker?"
|
|
|
|
> [§Forces] "Messages must be sent to the message broker in the order they were sent by the service."
|
|
|
|
> [§Solution] "The solution is for the service that sends the message to first store the message in the database as part of the transaction that updates the business entities."
|
|
|
|
> [§Solution — message relay] "A separate process then sends the messages to the message broker."
|
|
|
|
> [§Resulting context — benefits] "Messages are guaranteed to be sent if and only if the database transaction commits"
|
|
|
|
> [§Resulting context — drawbacks] "Potentially error prone since the developer might forget to publish the message/event after updating the database."
|
|
|
|
## Claims Extracted / 추출된 주장
|
|
|
|
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
|
|---|---|---|---|---|---|
|
|
| MSIO-OUTBOX-C1 | service command 는 **DB aggregate 변경 + 메시지 브로커로의 메시지 발행** 두 가지를 함께 해야 하는 경우가 일반적 (dual-write 컨텍스트) | [§Context] "A service command typically needs to create/update/delete aggregates in the database **and** send messages/events to a message broker." | `engineering-blog` | event-driven / SOA / microservices 의 command 흐름 | 모든 service command 가 메시지 발행을 동반해야 한다는 강제는 아님 — typically (일반적) |
|
|
| MSIO-OUTBOX-C2 | DB update 와 메시지 발행이 **atomic** 하지 않으면 data inconsistency / bug 가 발생할 수 있음 | [§Context] "The command must atomically update the database and send messages in order to avoid data inconsistencies and bugs." | `engineering-blog` | DB commit 과 broker publish 가 별개 트랜잭션인 모든 시나리오 | 어떤 종류의 inconsistency 가 어떤 빈도로 발생하는지의 정량적 근거는 본 인용에 없음 |
|
|
| MSIO-OUTBOX-C3 | 핵심 문제는 "**DB 와 메시지 브로커를 어떻게 atomic 하게 동시에 update 할 것인가**" | [§Problem] "How to atomically update the database and send messages to a message broker?" | `engineering-blog` | dual-write 문제 정의 | 2PC (XA) 같은 distributed transaction 이 부적절하다는 결론은 본 한 줄 인용으로 직접 입증 안 됨 — Forces 섹션과 결합 필요 |
|
|
| MSIO-OUTBOX-C4 | force: **메시지는 service 가 발행한 순서대로** 브로커에 전달되어야 함 | [§Forces] "Messages must be sent to the message broker in the order they were sent by the service." | `engineering-blog` | 순서 보장이 필요한 도메인 이벤트 (state machine 등) | 모든 메시징 시나리오가 strict ordering 을 요구한다는 의미는 아님 — 본 force 가 적용되는 시스템에서만 |
|
|
| MSIO-OUTBOX-C5 | 해법: 발행할 메시지를 **business entity 를 update 하는 동일 트랜잭션의 일부로 DB 에 먼저 저장** | [§Solution] "The solution is for the service that sends the message to first store the message in the database as part of the transaction that updates the business entities." | `engineering-blog` | OUTBOX 테이블 구현 시 | 메시지 저장 테이블이 반드시 "OUTBOX" 라는 이름이어야 한다는 강제는 아님 (관습일 뿐) |
|
|
| MSIO-OUTBOX-C6 | 별도 process (message relay) 가 저장된 메시지를 브로커로 발행 | [§Solution] "A separate process then sends the messages to the message broker." | `engineering-blog` | polling publisher / transaction log tailing 등 relay 구현 | relay 가 별도 OS 프로세스여야 한다는 강제는 아님 — 동일 서비스 내 별도 스레드/스케줄러도 일반적 |
|
|
| MSIO-OUTBOX-C7 | benefit: 메시지는 **DB 트랜잭션이 commit 된 경우에 한해 그리고 그 경우에만** 발행이 보장됨 (if and only if) | [§Resulting context — benefits] "Messages are guaranteed to be sent if and only if the database transaction commits" | `engineering-blog` | at-least-once delivery + 일관성 보장 평가 | exactly-once 까지 보장된다는 의미는 아님 — relay 가 동일 메시지를 재발행할 수 있으므로 consumer 측 idempotency 필요 |
|
|
| MSIO-OUTBOX-C8 | drawback: 개발자가 DB update 후 메시지/이벤트 발행을 **잊을 수 있어 error-prone** | [§Resulting context — drawbacks] "Potentially error prone since the developer might forget to publish the message/event after updating the database." | `engineering-blog` | outbox write 가 application code 의 명시적 호출에 의존하는 구현 | 모든 outbox 구현이 error-prone 하다는 의미는 아님 — 도메인 이벤트 자동 수집 (e.g., Spring Data domain events / aspect) 으로 완화 가능 |
|
|
|
|
## Usage Boundaries / 적용 경계
|
|
|
|
- **이 자료가 직접 증명하는 것**:
|
|
- `MSIO-OUTBOX-C1`~`C3`: dual-write 문제의 정의 + atomicity 요구
|
|
- `MSIO-OUTBOX-C4`: ordering force
|
|
- `MSIO-OUTBOX-C5`~`C6`: 해법의 두 축 (OUTBOX 저장 + 별도 relay)
|
|
- `MSIO-OUTBOX-C7`~`C8`: benefit (commit 과 발행의 if-and-only-if 보장) 과 drawback (forget-to-publish)
|
|
- **이 자료가 증명하지 않는 것**:
|
|
- 본 페이지가 **공식 vendor doc** 이라는 점 — microservices.io 는 Chris Richardson 의 personal pattern catalog. AWS/Spring/Confluent 등의 공식 채택을 의미하지 않음. strength `engineering-blog`.
|
|
- 특정 구현 (Debezium / Spring Modulith / Eventuate / 자체 polling) 이 정답이라는 결론
|
|
- 메시지 브로커가 반드시 Kafka 여야 한다는 점 (RabbitMQ / SQS / Pulsar 모두 동일 패턴 적용 가능)
|
|
- exactly-once delivery 보장 — `C7` 의 "if and only if" 는 DB-쪽 보장이며, consumer 측 idempotency 와 독립
|
|
- outbox 테이블 schema 의 정확한 컬럼 구성 (id, aggregate_id, type, payload, created_at 등은 관습)
|
|
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
|
- ca-tmpl 이 polling publisher / transaction log tailing 중 어느 변형을 default 로 채택할지 ([[raw/branch-notes/feature-domain-event-outbox-contract]])
|
|
- 도메인 이벤트 수집 메커니즘 (Aggregate.registerEvent → Repository.save 시 함께 outbox insert) 의 구체 설계
|
|
- consumer 측 idempotency 보장 정책
|
|
|
|
## 메모 / Notes
|
|
|
|
- 본 패턴은 *Microservices Patterns* (Chris Richardson, Manning 2018) 책에도 동일 내용 수록. 책이 더 상세하지만 본 페이지가 가장 자주 인용되는 단일 URL.
|
|
- microservices.io 가 personal blog 임에도 패턴 카탈로그로서 사실상 표준 참조로 사용되는 경우가 많음. 그러나 본 wiki 의 strength 분류 기준에서는 `engineering-blog` 가 정확 — 공식 vendor doc / 표준이 아니므로.
|
|
- "공식 best practice" 로 인용하려면 동일 패턴을 다루는 official-vendor-doc (예: AWS Prescriptive Guidance, Microsoft Cloud Design Patterns) 와 corroborate 해야 함.
|
|
|
|
## Related / 관련
|
|
|
|
- 같은 주제 다른 raw:
|
|
- [[raw/official-docs/cqrs-fowler-bliki]] (CQRS 와 자주 결합되는 패턴 — Fowler bliki)
|
|
- [[raw/official-docs/arch-hexagonal-cockburn]] (event publishing port 정의 기반)
|
|
- 이 자료를 인용하는 branch:
|
|
- [[raw/branch-notes/feature-repository-access-permission-contract]]
|
|
- [[raw/branch-notes/feature-domain-event-outbox-contract]]
|
|
- 인용하는 project:
|
|
- [[raw/project-notes/ca-skeleton-operational-contract]]
|
|
- 이 자료를 인용한 wiki 요약: (미작성)
|