---
id: backend-saga-choreography-vs-orchestration
title: Saga — Choreography vs Orchestration
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [backend, saga, distributed, vibe-coding]
tech_stack: { language: "TS / Python", applicable_to: ["Backend"] }
applied_in: []
aliases: [saga, choreography, orchestration, distributed transaction, compensating action, Temporal]
---

# Saga: Choreography vs Orchestration

> Microservice 가 distributed transaction X. **Saga = 작은 step + compensating action**. 두 style: choreography (event-driven) vs orchestration (central). Temporal / AWS Step Functions.

## 📖 핵심 개념
- 여러 service 의 transaction.
- Compensating action (undo) 가 필요.
- Choreography: event 발행 → 서로 listen.
- Orchestration: 중앙 (orchestrator) 가 step 지시.

## 💻 코드 패턴

### 문제 (no saga)
```
Order 처리:
1. Order DB 에 INSERT.
2. Inventory service: reserve item.
3. Payment service: charge card.
4. Shipping service: schedule.

Step 3 가 fail = ?
- Step 1, 2 가 rollback 안 됨 (다른 DB).
```

### Choreography (event-driven)
```
Order Service           Inventory          Payment           Shipping
   |                       |                  |                  |
   |-- OrderCreated ----->|                  |                  |
   |                       |-- ItemReserved ->|                  |
   |                       |                  |-- Paid ---------->|
   |                       |                  |                  |-- Shipped
```

```ts
// Order service
async function createOrder(data) {
  const order = await db.orders.insert(data);
  await eventBus.publish('OrderCreated', { orderId: order.id, items: data.items });
  return order;
}

// Inventory service
eventBus.on('OrderCreated', async (e) => {
  try {
    await reserve(e.items);
    await eventBus.publish('ItemsReserved', { orderId: e.orderId });
  } catch {
    await eventBus.publish('ReservationFailed', { orderId: e.orderId });
  }
});

// Payment service
eventBus.on('ItemsReserved', async (e) => {
  try {
    await charge(e.orderId);
    await eventBus.publish('OrderPaid', { orderId: e.orderId });
  } catch {
    await eventBus.publish('PaymentFailed', { orderId: e.orderId });
  }
});

// Compensation
eventBus.on('PaymentFailed', async (e) => {
  await releaseInventory(e.orderId);  // undo
  await cancelOrder(e.orderId);
});
```

→ 매 service 가 next step 알아.

### Orchestration (central)
```
Orchestrator
   |
   |-- 1. reserve() ----------> Inventory
   |   <- ItemsReserved
   |-- 2. charge() ----------> Payment
   |   <- Paid
   |-- 3. ship() ----------> Shipping
   |   <- Shipped
   |
   | (실패 시 compensate)
   |-- release() <- Inventory
```

```ts
// Orchestrator
async function processOrder(orderData) {
  let reserved = false, paid = false;
  try {
    await orderService.create(orderData);
    
    await inventory.reserve(orderData.items);
    reserved = true;
    
    await payment.charge(orderData.payment);
    paid = true;
    
    await shipping.schedule(orderData);
    return 'success';
  } catch (e) {
    if (paid) await payment.refund(orderData.payment);
    if (reserved) await inventory.release(orderData.items);
    await orderService.cancel(orderData.id);
    throw e;
  }
}
```

→ 1 곳 에서 모든 step + compensation.

### 비교
```
Choreography:
✓ Loose coupling.
✓ Service 가 독립.
✗ Logic 가 흩어짐 — debug 어려움.
✗ Cascade complexity.

Orchestration:
✓ 1 곳 logic.
✓ Visibility / monitoring.
✗ Orchestrator 가 SPOF.
✗ Service 가 orchestrator 알아.
```

→ 작은 = choreography. 복잡 = orchestration.

### Temporal (workflow engine)
```ts
import * as workflow from '@temporalio/workflow';

export async function orderWorkflow(order: Order) {
  const { reserve, charge, ship, release, refund, cancel } = workflow.proxyActivities({
    startToCloseTimeout: '1 minute',
    retry: { maximumAttempts: 3 },
  });
  
  let reserved = false, paid = false;
  try {
    await reserve(order.items);
    reserved = true;
    
    await charge(order.payment);
    paid = true;
    
    await ship(order);
  } catch (e) {
    if (paid) await refund(order.payment);
    if (reserved) await release(order.items);
    await cancel(order.id);
    throw e;
  }
}
```

→ Durable workflow. Retry / timeout / pause / resume.

### Temporal 의 강점
```
- Activity 가 fail = 자동 retry.
- Workflow 가 host 죽음 = 다른 host 가 resume.
- Time travel debugging.
- State 가 자동 persist.
- 매 step 가 visible (UI).
```

→ Saga 의 모던 방법.

### AWS Step Functions
```json
{
  "Comment": "Order saga",
  "StartAt": "ReserveItems",
  "States": {
    "ReserveItems": {
      "Type": "Task",
      "Resource": "arn:...:reserve",
      "Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "Cancel" }],
      "Next": "ChargePayment"
    },
    "ChargePayment": {
      "Type": "Task",
      "Resource": "arn:...:charge",
      "Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "ReleaseAndCancel" }],
      "Next": "Ship"
    },
    "Ship": { ... },
    "ReleaseAndCancel": { ... },
    "Cancel": { "Type": "Task", "Resource": "arn:...:cancel", "End": true }
  }
}
```

→ Visual / state machine. Lambda 친화.

### Compensating action 의 함정
```
- Idempotent 필수 (재시도 안전)
- 매 action 가 reversible
- Side effect (email 보낸 후 = unsend X)

→ 어떤 action 가 "최종 + compensate 안 됨" 식별:
  - Email 발송
  - 외부 API call
  - 영구 file write
```

### Idempotency key
```ts
async function reserveItems(items, idempotencyKey) {
  const existing = await db.reservations.findOne({ idempotencyKey });
  if (existing) return existing;  // 같은 key = 같은 결과
  
  return await db.reservations.insert({ items, idempotencyKey });
}
```

→ 재시도 가 중복 X.

### Outbox pattern
```sql
BEGIN;
  INSERT INTO orders (...);
  INSERT INTO outbox (event_type, payload) VALUES ('OrderCreated', '...');
COMMIT;

-- Background process 가 outbox → publish
```

→ Event 가 transaction 안. "DB write + event 둘 다" 안전.

→ [[Backend_Outbox_Pattern]].

### Async vs sync
```
Choreography = async (event).
Orchestration = sync (RPC) or async.

→ Async 가 scalability. Sync 가 simple debug.
```

### Eventual consistency
```
Saga = eventually consistent.
- Step 1 후 step 2 까지 시간 = 다른 view.
- "Cart 비어있는데 order 있음" 가능 (밀리초).

→ User 에 명시적 status (pending / processing / done).
```

### Failure 의 종류
```
Transient: retry 가능.
- Network blip
- Timeout
- 503

Permanent: compensate.
- Card declined
- Out of stock
- Validation error

→ Retry policy 가 다름.
```

### Compensation chain
```ts
// 매 step 가 reverse:
const compensations = [];

try {
  await reserve(items); compensations.push(() => release(items));
  await charge(payment); compensations.push(() => refund(payment));
  await ship(order); compensations.push(() => unship(order));
} catch (e) {
  for (const c of compensations.reverse()) await c();
  throw e;
}
```

→ Deterministic compensation order.

### Monitoring
```
- Saga ID (correlation)
- Step status (started / done / failed)
- Compensation status
- Duration / retry count

Tools: Temporal UI, AWS Step Functions visual.
```

### LLM use case
```
"Order workflow" → Temporal.
"Multi-step LLM agent" → Temporal.
- Each LLM call = activity (retry).
- State = workflow.

→ AI agent 의 production framework.
```

### Real-world
- **Netflix**: Conductor (orchestrator).
- **Uber**: Cadence / Temporal.
- **Airbnb**: SQS + manual orchestration.
- **AWS internal**: Step Functions.

### Anti-pattern: Distributed transaction (XA, 2PC)
```
2-phase commit 가 cross-DB transaction:
- Prepare → Commit
- 모든 participant 가 동의 시 commit

→ 매우 느림 + 깨짐 자주.
Microservice 가 안 사용.
Saga 가 답.
```

## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| 작은 system | Monolith transaction |
| 2-3 service | Choreography |
| 5+ service | Orchestration |
| Production saga | Temporal / Step Functions |
| Long-running (days) | Temporal |
| Lambda + simple | Step Functions |
| Real-time saga | Temporal / Cadence |

## ❌ 안티패턴
- **2PC (cross DB)**: 깨짐.
- **No idempotency**: 중복 effect.
- **Compensation 안 reversible action**: stuck.
- **Choreography 큰 system**: spaghetti.
- **No correlation ID**: debug 불가.
- **Manual saga (no engine)**: edge case 많음.
- **Eventual consistency 무시**: UI 깨짐.

## 🤖 LLM 활용 힌트
- Saga = compensating action 의 chain.
- Temporal 가 modern saga (durable, retry, UI).
- Choreography 가 작은 system, orchestration 가 큰.
- Idempotency + correlation ID 필수.

## 🔗 관련 문서
- [[Backend_Saga_Patterns]]
- [[Backend_Outbox_Pattern]]
- [[Backend_Idempotency_Keys]]
