目次を表示する

EvansとVernonで学ぶDDD

[共通] Context Map(コンテキストマップ)── コンテキスト間の関係を設計する

コンテキストマップとは何か

境界づけられたコンテキストを複数定義すると、次の問いが生まれる。「それぞれのコンテキストはどのように連携するのか」「あるコンテキストの変更が別のコンテキストに影響を与えるのはどのような経路か」。Evansはこの問いに答えるために、コンテキストマップという概念を導入した。

コンテキストマップとは、複数の境界づけられたコンテキストの関係・依存関係・統合方法を可視化した地図である。Evansは青本(Domain-Driven Design)の中で、コンテキストマップをシステム全体を俯瞰するための設計ツールとして位置づけた。コードの詳細ではなく、コンテキスト同士がどのような力学のもとで連携しているかを表す。

コンテキストが2つであれば連携の把握は容易だが、採用管理システムのように4つ以上になると「どのコンテキストがどのコンテキストに依存しているか」が見えなくなる。依存関係が暗黙的なまま実装が進むと、あるコンテキストの内部変更が予期しない範囲に波及し、障害の原因になる。コンテキストマップはこの問題を予防するための設計上の取り決めである。


主要なContext Mappingパターン

Evansはコンテキスト間の関係を類型化するため、複数のContext Mappingパターンを定義した。以下に採用管理システムを題材として4つのパターンを説明する。

腐敗防止層(Anti-Corruption Layer)

外部システムや他コンテキストのモデルが、自コンテキストのモデルに直接侵食することを防ぐための変換層である。

採用管理システムでは、候補者管理コンテキストが社内の既存人事システム(レガシーシステム)と連携する場面がある。このレガシーシステムは applicant_idfirst_nmlast_nm といった列名を持つ独自のデータ構造を持っている。この構造をそのまま候補者管理コンテキストに取り込むと、レガシーシステムの命名規則や概念がコンテキスト内に混入する。

腐敗防止層はこの汚染を防ぐ。外部データを受け取り、自コンテキストの言語(ユビキタス言語)に則った形に変換してから内部に渡す。

顧客/供給者(Customer/Supplier)

2つのコンテキストが上流・下流の関係にあるとき、下流コンテキストが上流コンテキストのAPIや出力に依存するパターンである。ただし、下流(顧客)は上流(供給者)のロードマップに影響を与える立場にあり、単なる一方的な依存ではない。

採用管理システムでは、通知コンテキストが選考管理コンテキストの「顧客」にあたる。選考管理コンテキストが「内定通知を発行した」というイベントを提供し、通知コンテキストはそれを受けてメール送信を実行する。通知コンテキストは選考管理コンテキストのAPIやイベント定義に従って動作するが、必要に応じてAPIの変更を要求できる立場でもある。

順応者(Conformist)

下流コンテキストが上流コンテキストのモデルをそのまま受け入れるパターンである。下流側が上流側に交渉できる立場になく、モデルの変換コストも正当化できない場合に選択される。

採用管理システムでは、候補者管理コンテキストが外部の求職者データベースサービス(SaaSなど)を利用する場合がこれに該当する。サービス側のデータ構造や用語体系をそのまま取り込み、自コンテキストのモデルを外部サービスに合わせる。独自モデルを維持するよりも、外部サービスへの適合を優先する判断である。

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

あるコンテキストが複数の他コンテキストから利用されることを想定し、明確に定義された公開APIを提供するパターンである。

採用管理システムでは、選考管理コンテキストが公開ホストサービスとして機能する。求人管理コンテキスト、候補者管理コンテキスト、通知コンテキストのいずれもが選考状態を参照する必要があるため、選考管理コンテキストは安定したインタフェースを公開する責務を持つ。APIの変更は下流コンテキスト全体に影響するため、バージョニングや後方互換性の維持が求められる。


採用管理システムのコンテキストマップ

4つのコンテキストの関係を整理すると次のようになる。

上流コンテキストパターン下流コンテキスト
選考管理顧客/供給者(Customer/Supplier)通知
選考管理公開ホストサービス(Open Host Service)求人管理・候補者管理・通知
外部人事システム腐敗防止層(Anti-Corruption Layer)候補者管理
外部求職者DBサービス順応者(Conformist)候補者管理

選考管理コンテキストはシステムの中核であり、複数のコンテキストから参照される。そのため公開ホストサービスとして安定したAPIを維持する必要がある。候補者管理コンテキストは外部との接点が多く、腐敗防止層と順応者の両パターンを適用している点が特徴である。


TypeScriptで腐敗防止層を実装する

レガシー人事システムのデータ構造を候補者管理コンテキストのモデルへ変換するアダプタの実装例を示す。

// 外部システムのデータ構造(そのまま取り込みたくない)
interface LegacyApplicantDTO {
  applicant_id: string;
  first_nm: string;
  last_nm: string;
  birth_dt: string;      // "YYYYMMDD" 形式
  email_addr: string;
}

// 自コンテキストの値オブジェクト群
class CandidateId {
  private constructor(private readonly value: string) {}

  static from(raw: string): CandidateId {
    if (!raw || raw.trim().length === 0) {
      throw new Error("CandidateId は空にできない");
    }
    return new CandidateId(raw.trim());
  }

  toString(): string {
    return this.value;
  }
}

class FullName {
  private constructor(
    private readonly firstName: string,
    private readonly lastName: string
  ) {}

  static create(first: string, last: string): FullName {
    if (!first || !last) {
      throw new Error("氏名に空文字は使用できない");
    }
    return new FullName(first.trim(), last.trim());
  }

  get displayName(): string {
    return `${this.lastName} ${this.firstName}`;
  }
}

class CandidateProfile {
  constructor(
    public readonly id: CandidateId,
    public readonly name: FullName,
    public readonly email: string
  ) {}
}

// 腐敗防止層: 外部人事システムのデータを自コンテキストのモデルに変換
class LegacyHRSystemAdapter {
  toCandidateProfile(legacyData: LegacyApplicantDTO): CandidateProfile {
    return new CandidateProfile(
      CandidateId.from(legacyData.applicant_id),
      FullName.create(legacyData.first_nm, legacyData.last_nm),
      legacyData.email_addr
    );
  }
}

LegacyHRSystemAdapter はインフラ層に配置する。ドメイン層の CandidateProfile はレガシーシステムの存在を知らない。外部データの変換責務はアダプタに集約されているため、レガシーシステムのデータ構造が変更されても影響範囲はアダプタ内に留まる。


VernonによるContext Mappingパターンの整理

Vernonは赤本(Implementing Domain-Driven Design)の中で、Evansのパターン群を「上流/下流」という軸でより体系的に整理した。

Evansのパターン定義はそれぞれのパターンの特性を記述したものだが、Vernonはこれらを「どのコンテキストが変更の主導権を持ち、どのコンテキストがそれに適応するか」という非対称な力学の観点から再整理した。上流コンテキストの変更は下流コンテキストに影響を与えるが、下流コンテキストの変更は上流コンテキストに影響しない。この方向性の非対称さがコンテキスト間の依存関係の本質であるというのがVernonの強調点である。

この観点から見ると、腐敗防止層は「上流の変更から下流を守る防壁」として機能し、顧客/供給者は「下流が上流の変更プロセスに関与できる合意の形」として機能する。順応者は下流が上流に完全に従属しており、公開ホストサービスは上流が複数の下流を想定して変更コストを明示的に管理する形態として整理できる。

Vernonのアプローチはパターン選択の判断基準を明確にする。「このコンテキストは変更に対してどの立場にあるか」を問うことで、適切なパターンを選択できる。


インフォグラフィック

サマリー

章末図: 採用管理システムのコンテキストマップ全体図

graph TD
  subgraph 内部コンテキスト
    JM[求人管理]
    SM[選考管理]
    CM[候補者管理]
    NF[通知]
  end

  subgraph 外部システム
    LHR[レガシー人事システム]
    EXT[外部求職者DBサービス]
  end

  SM -- "公開ホストサービス\n(Open Host Service)\n[上流]" --> JM
  SM -- "公開ホストサービス\n(Open Host Service)\n[上流]" --> CM
  SM -- "顧客/供給者\n(Customer/Supplier)\n[上流]" --> NF

  LHR -- "[上流]" --> ACL["腐敗防止層\n(Anti-Corruption Layer)"]
  ACL -- "[変換後]" --> CM

  EXT -- "順応者\n(Conformist)\n[上流]" --> CM

矢印は上流から下流への依存の方向を示す。選考管理コンテキストが内部の中心的な上流として機能し、候補者管理コンテキストは外部との接点において2種類の異なるパターンを使い分けている。腐敗防止層は独立したノードとして図示しており、外部モデルと内部モデルの間に明示的な変換点が存在することを表している。