ストーリー
ミッション概要
| ミッション | テーマ | 目安時間 |
|---|---|---|
| Mission 1 | 注文フローのイベントスキーマ設計 | 15分 |
| Mission 2 | Choreography Saga の設計 | 15分 |
| Mission 3 | Orchestration Saga の設計 | 15分 |
| Mission 4 | 障害シナリオの対応設計 | 15分 |
前提シナリオ
ShopMasterの注文フローをイベント駆動で設計します。関連サービス:注文、在庫、決済、配送、通知の5サービス。
Mission 1: 注文フローのイベントスキーマ設計(15分)
要件
注文確定から配送完了までのドメインイベントをCloudEvents仕様で定義してください。
解答例
// 1. 注文確定
const orderConfirmed = {
specversion: '1.0',
type: 'order.confirmed',
source: '/order-service',
id: 'evt-001',
time: '2025-01-15T10:00:00Z',
data: {
orderId: 'ord-789',
customerId: 'cust-456',
items: [{ productId: 'prod-001', quantity: 2, unitPrice: 5000 }],
totalAmount: 10000,
currency: 'JPY',
shippingAddress: { prefecture: '東京都', city: '渋谷区' },
},
};
// 2. 在庫引当完了
const stockReserved = {
type: 'inventory.stock.reserved',
data: {
orderId: 'ord-789',
reservationId: 'res-001',
items: [{ productId: 'prod-001', quantity: 2, reserved: true }],
expiresAt: '2025-01-15T10:15:00Z',
},
};
// 3. 決済完了
const paymentCompleted = {
type: 'payment.completed',
data: {
orderId: 'ord-789',
paymentId: 'pay-001',
amount: 10000,
method: 'credit_card',
},
};
// 4. 出荷開始
const shipmentStarted = {
type: 'shipping.shipment.started',
data: {
orderId: 'ord-789',
shipmentId: 'ship-001',
trackingNumber: 'TRK-12345',
carrier: 'yamato',
estimatedDelivery: '2025-01-17',
},
};
// 5. 配送完了
const shipmentDelivered = {
type: 'shipping.shipment.delivered',
data: {
orderId: 'ord-789',
shipmentId: 'ship-001',
deliveredAt: '2025-01-17T14:30:00Z',
},
};
Mission 2: Choreography Saga の設計(15分)
要件
注文フローをChoreography Sagaで設計し、各サービスが購読するイベントと発行するイベントを定義してください。
解答例
| サービス | 購読イベント | 処理 | 発行イベント |
|---|---|---|---|
| 注文 | (API: 注文リクエスト) | 注文レコード作成 | order.confirmed |
| 在庫 | order.confirmed | 在庫引当 | inventory.stock.reserved / inventory.stock.insufficient |
| 決済 | inventory.stock.reserved | 決済処理 | payment.completed / payment.failed |
| 配送 | payment.completed | 出荷手配 | shipping.shipment.started |
| 通知 | 全イベント | メール/プッシュ送信 | (なし) |
| 注文 | payment.completed | ステータス更新 → PAID | (状態更新のみ) |
| 注文 | shipping.shipment.started | ステータス更新 → SHIPPED | (状態更新のみ) |
補償フロー:
| 失敗イベント | 補償アクション |
|---|---|
| inventory.stock.insufficient | order.cancelled を発行 |
| payment.failed | inventory.stock.released を発行→ order.cancelled |
Mission 3: Orchestration Saga の設計(15分)
要件
同じ注文フローをOrchestration Sagaで設計し、オーケストレーターの状態遷移図を作成してください。
解答例
graph TD
S["STARTED"] --> RS["RESERVING_STOCK"]
RS --> SR["STOCK_RESERVED"]
RS --> SI["STOCK_INSUFFICIENT"]
SR --> CP["CHARGING_PAYMENT"]
SI --> C1["CANCELLED"]
CP --> PC["PAYMENT_CHARGED"]
CP --> PF["PAYMENT_FAILED"]
PC --> AS["ARRANGING_SHIPMENT"]
PF --> CS["COMPENSATING_STOCK"]
AS --> SA["SHIPMENT_ARRANGED"]
CS --> C2["CANCELLED"]
SA --> Done["COMPLETED"]
style S fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e40af
style RS fill:#fef3c7,stroke:#d97706,color:#92400e
style SR fill:#d1fae5,stroke:#059669,color:#065f46
style SI fill:#fee2e2,stroke:#dc2626,color:#991b1b
style CP fill:#fef3c7,stroke:#d97706,color:#92400e
style C1 fill:#fee2e2,stroke:#dc2626,color:#991b1b
style PC fill:#d1fae5,stroke:#059669,color:#065f46
style PF fill:#fee2e2,stroke:#dc2626,color:#991b1b
style AS fill:#fef3c7,stroke:#d97706,color:#92400e
style CS fill:#fee2e2,stroke:#dc2626,color:#991b1b
style SA fill:#d1fae5,stroke:#059669,color:#065f46
style C2 fill:#fee2e2,stroke:#dc2626,color:#991b1b
style Done fill:#d1fae5,stroke:#059669,stroke-width:3px,color:#065f46
// Orchestrator実装
class OrderSagaOrchestrator {
async handle(state: SagaState, event: DomainEvent): Promise<SagaState> {
switch (state.status) {
case 'STARTED':
await this.inventoryClient.reserveStock(state.orderId, state.items);
return { ...state, status: 'RESERVING_STOCK' };
case 'RESERVING_STOCK':
if (event.type === 'inventory.stock.reserved') {
await this.paymentClient.charge(state.orderId, state.amount);
return { ...state, status: 'CHARGING_PAYMENT' };
}
if (event.type === 'inventory.stock.insufficient') {
await this.orderClient.cancel(state.orderId);
return { ...state, status: 'CANCELLED' };
}
break;
case 'CHARGING_PAYMENT':
if (event.type === 'payment.completed') {
await this.shippingClient.arrange(state.orderId);
return { ...state, status: 'ARRANGING_SHIPMENT' };
}
if (event.type === 'payment.failed') {
await this.inventoryClient.releaseStock(state.orderId);
return { ...state, status: 'COMPENSATING_STOCK' };
}
break;
}
return state;
}
}
Mission 4: 障害シナリオの対応設計(15分)
要件
以下の3つの障害シナリオに対する対応を設計してください。
- 決済サービスがタイムアウト(応答なし)
- 配送手配後に顧客がキャンセル要求
- 在庫引当後にプロダクトが値上げされた
解答例
シナリオ1: 決済タイムアウト
対応: リトライ + タイムアウト後に補償
1. 30秒タイムアウト + 3回リトライ(指数バックオフ)
2. 全リトライ失敗 → 在庫引当を解除(compensate)
3. 注文ステータスを「決済失敗」に更新
4. 顧客に通知「決済処理に失敗しました。再度お試しください」
5. 冪等キーで重複決済を防止
シナリオ2: 配送手配後のキャンセル
対応: 逆順の補償トランザクション
1. 配送キャンセル → shipping.cancelled
2. 決済返金 → payment.refunded
3. 在庫引当解除 → inventory.released
4. 注文キャンセル → order.cancelled
注意: 配送が既に出荷済みの場合は返品フローに分岐
シナリオ3: 引当後の値上げ
対応: 注文確定時の価格を記録(イベントソーシング)
- 注文イベントに確定時の価格を含める
- 在庫引当は数量のみ管理(価格は注文サービスの責務)
- 値上げは新規注文にのみ適用
- 既存注文は確定時価格を保証(契約)
まとめ
| ポイント | 内容 |
|---|---|
| イベントスキーマ | CloudEvents 仕様で統一 |
| Choreography | 自律型、小規模向き |
| Orchestration | 集中管理、大規模向き |
| 障害対応 | リトライ+補償+冪等性で堅牢化 |
チェックリスト
- CloudEvents仕様でイベントスキーマを設計できた
- Choreography Saga を設計できた
- Orchestration Saga を状態遷移図で設計できた
- 障害シナリオの対応を設計できた
次のステップへ
次はチェックポイントクイズでイベント駆動アーキテクチャの理解度を確認します。
推定読了時間: 60分