LESSON 40分

ストーリー

高橋アーキテクト
Bounded Contextを特定したら、次はそれらの関係を可視化する
高橋アーキテクト
コンテキストマップは、Bounded Context間の技術的な関係だけでなく、チーム間の政治的・組織的な関係も表すんだ。コンウェイの法則を聞いたことがあるかい?
あなた
“組織の構造がシステムの構造に反映される”ってやつですか?
高橋アーキテクト
その通り。だからコンテキストマップは、技術と組織の両方を考慮して描く必要がある

コンテキストマップとは

コンテキストマップは、Bounded Context間の関係を可視化した図です。

┌─────────────┐        ┌─────────────┐
│  注文BC      │ ─U/D─→ │  決済BC      │
│  (チームA)   │        │  (チームB)   │
└──────┬──────┘        └─────────────┘

       │ Published
       │ Language

┌─────────────┐        ┌─────────────┐
│  在庫BC      │ ─ACL─→ │  外部配送API  │
│  (チームA)   │        │  (外部企業)   │
└─────────────┘        └─────────────┘

主要な関係パターン

1. Shared Kernel(共有カーネル)

2つのBCが共通のドメインモデルを共有します。

// 共有パッケージ
// shared-kernel/Money.ts
export class Money {
  // 注文BCも決済BCも同じMoneyを使う
  static of(amount: number, currency: string): Money { /* ... */ }
  add(other: Money): Money { /* ... */ }
}
利点注意点
コードの重複を避けられる変更時に両チームの合意が必要
一貫性が保たれる結合度が高くなる

2. Customer-Supplier(顧客-供給者)

上流(Supplier)が下流(Customer)にサービスを提供します。

┌─────────────┐          ┌─────────────┐
│  注文BC      │ ───────→ │  配送BC      │
│  (Supplier)  │          │  (Customer)  │
│  上流        │          │  下流        │
└─────────────┘          └─────────────┘
下流のニーズを上流が考慮してAPIを設計する

3. Conformist(順応者)

下流が上流のモデルにそのまま従います。交渉力がない場合のパターンです。

// 外部決済サービスのモデルにそのまま合わせる
class StripePaymentGateway implements PaymentGateway {
  async charge(amount: Money, method: PaymentMethod): Promise<PaymentResult> {
    // Stripeの仕様に合わせるしかない
    const intent = await stripe.paymentIntents.create({
      amount: amount.toCents(),
      currency: amount.currency.toLowerCase(),
    });
    return this.toPaymentResult(intent);
  }
}

4. Anti-Corruption Layer(腐敗防止層, ACL)

外部システムのモデルが自分のドメインを汚染しないように、変換層を設けます。

// ACL: 外部配送APIのモデルを自分のドメインモデルに変換
class ShippingApiAdapter implements ShippingService {
  constructor(private externalApi: ExternalShippingApi) {}

  async createShipment(order: Order): Promise<ShipmentId> {
    // 外部APIの型 → 自ドメインの型 への変換
    const externalRequest = {
      // 外部APIは "recipient" という言葉を使う
      recipient: {
        name: order.shippingAddress.recipientName,
        addr1: order.shippingAddress.line1,
        addr2: order.shippingAddress.line2,
        zip: order.shippingAddress.postalCode,
      },
      // 外部APIは "parcels" という単位
      parcels: order.items.map(item => ({
        sku: item.productId,
        qty: item.quantity,
        weight_g: item.weightGrams,
      })),
    };

    const externalResponse = await this.externalApi.create(externalRequest);

    // 外部APIの応答 → 自ドメインの型 への変換
    return ShipmentId.fromString(externalResponse.tracking_number);
  }
}

5. Published Language(公開言語)

Bounded Context間で共通のデータ形式を公開します。

// 注文BCが発行するイベント(Published Language)
// 他のBCはこの形式でイベントを受け取る
interface OrderCreatedEventPayload {
  eventType: 'order.created';
  version: '1.0';
  data: {
    orderId: string;
    customerId: string;
    items: Array<{
      productId: string;
      quantity: number;
      unitPrice: number;
      currency: string;
    }>;
    totalAmount: number;
    currency: string;
    createdAt: string; // ISO 8601
  };
}

6. Open Host Service(公開ホストサービス)

標準的なプロトコルやAPIでサービスを公開します。

// REST APIとして公開(Open Host Service)
// GET /api/orders/:id
// POST /api/orders
// DELETE /api/orders/:id
// → OpenAPI/Swagger仕様で文書化

関係パターンの選択

状況推奨パターン
2チームが密接に協力Shared Kernel
上流が下流のニーズを考慮Customer-Supplier
外部サービスで交渉力なしConformist
外部モデルの汚染を防ぎたいAnti-Corruption Layer
多くのBCにイベントを共有Published Language
APIとしてサービスを公開Open Host Service

コンウェイの法則

「システムの構造は、それを設計した組織のコミュニケーション構造を反映する」

組織構造:                    システム構造:
┌─────┐    ┌─────┐          ┌─────────┐    ┌─────────┐
│TeamA│────│TeamB│    →     │ 注文BC  │────│ 決済BC  │
└──┬──┘    └─────┘          └────┬────┘    └─────────┘
   │                             │
┌──▼──┐                    ┌────▼────┐
│TeamA│              →     │ 在庫BC  │
│(兼任)│                    │         │
└─────┘                    └─────────┘

チーム境界とBounded Contextの境界を合わせることで、チーム間のコミュニケーションコストを最小化できます。


実践: コンテキストマップの描き方

1. Bounded Contextを四角形で描く
2. チーム名を記載する
3. 矢印で依存の方向を示す
4. 関係パターンのラベルを付ける

┌─────────────────┐
│   ECサイト        │
│                  │
│  ┌──────┐  U/D  ┌──────┐
│  │注文BC│──────→│決済BC│
│  │TeamA │       │TeamB │
│  └──┬───┘       └──────┘
│     │ PL
│     │
│  ┌──▼───┐  ACL  ┌──────┐
│  │在庫BC│──────→│配送API│
│  │TeamA │       │外部   │
│  └──────┘       └──────┘
│                  │
└─────────────────┘

U/D = Upstream/Downstream
PL = Published Language
ACL = Anti-Corruption Layer

まとめ

ポイント内容
コンテキストマップBC間の関係を可視化する図
6つの関係パターンSK, CS, CF, ACL, PL, OHS
ACL外部モデルの汚染防止層(最も重要)
コンウェイの法則組織構造がシステム構造に反映される
チーム境界BCの境界とチーム境界を合わせる

チェックリスト

  • コンテキストマップの描き方を理解した
  • 6つの関係パターンをそれぞれ説明できる
  • ACLの必要性と実装方法を理解した
  • コンウェイの法則とBCの関係を理解した

次のステップへ

次は演習です。ここまで学んだDDDの知識を使って、実際のドメインをモデリングしてみましょう。


推定読了時間: 40分