EXERCISE 60分

ストーリー

佐藤CTO
理論は十分だ。ShopMasterの境界を自分の手で設計してみよう
佐藤CTO
正解は一つではない。大切なのは”なぜそこで切ったか”を説明できることだ

ミッション概要

ミッションテーマ目安時間
Mission 1Bounded Context の特定15分
Mission 2コンテキストマップの作成15分
Mission 3API契約の設計15分
Mission 4データベース分割計画15分

前提シナリオ

ShopMasterの主要ビジネスプロセスは以下の通りです:

  1. 商品管理: 出品者が商品を登録・編集・公開する
  2. 検索・閲覧: ユーザーが商品を検索し、詳細を閲覧する
  3. カート・注文: ユーザーがカートに追加し、注文を確定する
  4. 決済: クレジットカード/コンビニ支払い処理
  5. 在庫管理: 在庫確認・引当・補充管理
  6. 配送: 出荷指示、追跡番号管理、配送状況通知
  7. レビュー: 商品レビューの投稿・表示
  8. 顧客管理: 会員登録、プロフィール、購入履歴
  9. 通知: メール、プッシュ通知、SMS
  10. マーケティング: クーポン、キャンペーン、ポイント

Mission 1: Bounded Contextの特定(15分)

要件

上記のビジネスプロセスをBounded Contextにグルーピングし、各Contextの責務とサブドメイン分類(Core/Supporting/Generic)を定義してください。

解答例
Bounded Context責務サブドメイン理由
Catalog商品情報管理・検索Supporting商品の正本管理
Orderカート・注文ライフサイクルCore収益の中核プロセス
Payment決済処理・返金GenericStripe等で外部化可能
Inventory在庫引当・補充Supporting注文を支える固有機能
Shipping配送・追跡Supporting配送パートナー連携
Customer会員・認証・プロフィールGeneric汎用的な認証基盤
Reviewレビュー・評価Supporting購買判断を支援
Notification通知配信Generic汎用メッセージング
Marketingクーポン・ポイントCore競争優位の差別化

設計判断の根拠:

  • OrderをCoreとした理由: ECの本質は「注文→決済→配送」のフローであり、このフローの最適化が競争優位になる
  • MarketingをCoreとした理由: クーポン・ポイント施策はLTV向上の鍵であり、独自のロジックが必要

Mission 2: コンテキストマップの作成(15分)

要件

Mission 1で特定したBounded Context間の関係をContext Mappingパターンで定義してください。

解答例
graph TD
    CAT["Catalog"] -->|OHS/PL| ORD["Order"]
    INV["Inventory"] -->|Customer/Supplier| ORD
    ORD -->|ACL| PAY["Payment<br/>(Stripe)"]
    ORD -->|C/S| SHP["Shipping"]
    ORD -->|Partnership| MKT["Marketing"]
    REV["Review"] -->|Conformist| CAT
    NOTIFY["Notification<br/>← 全Contextからイベント受信"] -.->|Published Language| ORD
    NOTIFY -.-> CAT
    NOTIFY -.-> PAY
    NOTIFY -.-> SHP
    CUST["Customer"] -->|OHS| ALL["全サービス"]

    classDef core fill:#d4edda,stroke:#28a745,color:#333
    classDef ext fill:#fff3cd,stroke:#f0ad4e,color:#333
    classDef generic fill:#e8f4fd,stroke:#2196f3,color:#333
    class ORD,MKT core
    class PAY,CUST,NOTIFY generic
    class CAT,INV,SHP,REV ext

関係性の選択理由:

ペアパターン理由
Order → PaymentACLStripe APIの変更から保護
Order ↔ MarketingPartnership注文時のクーポン適用は密連携が必要
Order → InventoryCustomer/Supplier注文が在庫に要求を出す
Review → CatalogConformistレビューはカタログのモデルに従う
NotificationPublished Language標準イベントフォーマットで疎結合

Mission 3: API契約の設計(15分)

要件

Order Context と Inventory Context 間の API 契約を設計してください(在庫確認・引当のフロー)。

解答例

同期API(在庫確認):

# OpenAPI 仕様
paths:
  /inventory/{productId}/availability:
    get:
      summary: 在庫確認
      parameters:
        - name: productId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          content:
            application/json:
              schema:
                type: object
                properties:
                  productId:
                    type: string
                  available:
                    type: boolean
                  quantity:
                    type: integer
                  reservableUntil:
                    type: string
                    format: date-time

非同期イベント(在庫引当):

// 注文確定 → 在庫引当リクエスト(イベント)
interface StockReservationRequested {
  type: 'stock.reservation.requested';
  orderId: string;
  items: Array<{
    productId: string;
    quantity: number;
  }>;
  timestamp: string;
}

// 在庫引当結果(イベント)
interface StockReservationCompleted {
  type: 'stock.reservation.completed';
  orderId: string;
  status: 'reserved' | 'insufficient';
  items: Array<{
    productId: string;
    quantity: number;
    reserved: boolean;
  }>;
  expiresAt: string;  // 引当の有効期限
  timestamp: string;
}

設計判断: 在庫確認は同期(即時応答が必要)、引当は非同期(Sagaの一部として結果整合性で処理)。


Mission 4: データベース分割計画(15分)

要件

Order/Inventory/Catalog の3サービスのDB分割計画を段階的に策定してください。

解答例

Phase 1: Schema分離(2週間)

-- 共有DBをSchema分離
CREATE SCHEMA order_schema;
CREATE SCHEMA inventory_schema;
CREATE SCHEMA catalog_schema;

-- テーブルを適切なSchemaに移動
ALTER TABLE orders SET SCHEMA order_schema;
ALTER TABLE order_items SET SCHEMA order_schema;
ALTER TABLE products SET SCHEMA catalog_schema;
ALTER TABLE stock_levels SET SCHEMA inventory_schema;

-- 互換性のためにViewを作成
CREATE VIEW public.orders AS SELECT * FROM order_schema.orders;

Phase 2: JOIN解消(4週間)

// Before: SQL JOIN
// SELECT o.*, p.name FROM orders o JOIN products p ...

// After: API Composition
class OrderDetailService {
  async getOrderDetail(orderId: string) {
    const order = await this.orderRepo.findById(orderId);
    const productIds = order.items.map(i => i.productId);
    const products = await this.catalogClient.getProducts(productIds);
    return this.compose(order, products);
  }
}

Phase 3: 物理DB分離(4週間)

サービスDB選定理由
OrderPostgreSQLトランザクション整合性が重要
InventoryPostgreSQL + Redis在庫数はRedisでキャッシュ
CatalogPostgreSQL + Elasticsearch全文検索が必要

データ同期戦略:

graph LR
    ODB["Order DB"] -->|CDC Debezium| K1["Kafka"] -->|注文実績| CDB["Catalog DB"]
    CDB -->|CDC| K2["Kafka"] -->|商品名キャッシュ| ODB2["Order DB"]
    IDB["Inventory DB"] -->|CDC| K3["Kafka"] -->|在庫状況表示| CDB2["Catalog DB"]

    style ODB fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style ODB2 fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style CDB fill:#d1fae5,stroke:#059669,color:#065f46
    style CDB2 fill:#d1fae5,stroke:#059669,color:#065f46
    style IDB fill:#fef3c7,stroke:#d97706,color:#92400e
    style K1 fill:#f3e8ff,stroke:#7c3aed,color:#5b21b6
    style K2 fill:#f3e8ff,stroke:#7c3aed,color:#5b21b6
    style K3 fill:#f3e8ff,stroke:#7c3aed,color:#5b21b6

まとめ

ポイント内容
Context特定ビジネスプロセスからBounded Contextを導出
Context Map関係性パターンで協調方法を定義
API契約同期/非同期を使い分けて設計
DB分割Schema分離→JOIN解消→物理分離の3段階

チェックリスト

  • Bounded Context を特定しサブドメイン分類できた
  • Context Map で関係性を定義できた
  • 同期/非同期のAPI契約を設計できた
  • 段階的なDB分割計画を策定できた

次のステップへ

次はチェックポイントクイズでサービス境界設計の理解度を確認します。


推定読了時間: 60分