ドメイン駆動設計(DDD: Domain-Driven Design)は、Eric Evans が著書 Domain-Driven Design: Tackling Complexity in the Heart of Software(2003、邦訳『エリック・エヴァンスのドメイン駆動設計』)で提唱したソフトウェア設計の考え方。
ソフトウェアが対象とする問題領域(ドメイン)についての知識をモデルとして整理し、そのモデルを中心に据えて設計・実装を進めることで、複雑なビジネスロジックに立ち向かうことを目的とする。
Domain-Driven Design is an approach to software development that centers the development on programming a domain model that has a rich understanding of the processes and rules of a domain.
要点:
ドメインエキスパート(業務に詳しい人)と開発者が協働し、共通の言葉(ユビキタス言語)でモデルを作る
モデルとコードを一致させる(モデル駆動設計)
大きく「戦略的設計」(モデルの分割・チーム間の関係)と「戦術的設計」(実装パターン)に分かれる
ユビキタス言語(Ubiquitous Language)¶
ドメインエキスパートと開発者が共有する、ドメインモデルに基づいた共通言語。会話・ドキュメント・コード(クラス名・メソッド名)のすべてで同じ用語を使う。
開発者だけの用語(テーブル名や技術用語)とビジネス側だけの用語に分裂していると、翻訳コストが発生し、モデルと実装が乖離していく
用語の揺れや曖昧さが見つかったら、それはモデルの綻びのサインであり、モデルとコードを更新する機会になる
ユビキタス言語は境界づけられたコンテキストごとに定義される(コンテキストが違えば同じ単語でも意味が違ってよい)
戦略的設計(Strategic Design)¶
システム全体をどうモデルに分割し、モデル同士・チーム同士の関係をどう扱うかを決める、大きな粒度の設計。
境界づけられたコンテキスト(Bounded Context)¶
1つのモデルが一貫した意味を持つ適用範囲の境界。同じ「商品」という言葉でも、販売コンテキストでは価格やカタログ情報が重要で、在庫コンテキストでは保管場所や数量が重要、というように意味が異なる。
システム全体を1つの統一モデルで表現しようとせず、コンテキストごとにモデルを分けて、それぞれの内部で一貫性を保つ。マイクロサービスのサービス分割の理論的根拠としてもよく参照される。
コンテキストマップ(Context Map)¶
コンテキスト間の関係を可視化した図。関係のパターンとして以下などが挙げられている。
共有カーネル(Shared Kernel):モデルの一部を複数チームで共有する
顧客/供給者(Customer/Supplier):上流チームが下流チームの要求を考慮する
順応者(Conformist):下流が上流のモデルにそのまま従う
腐敗防止層(Anticorruption Layer):外部システムのモデルが自分のモデルを侵食しないよう、変換層を挟む
公開ホストサービス(Open Host Service)/公表された言語(Published Language):多数の利用者向けに公開プロトコルを定義する
別々の道(Separate Ways):統合せず独立させる
コアドメイン(Core Domain)¶
事業の競争力の源泉となる、最も価値のある領域。ここに最良の人材と労力を集中し、それ以外の汎用サブドメイン(認証など、どの会社でも同じもの)や支援サブドメインは既製品の利用や簡素な実装で済ませる。
戦術的設計(Tactical Design)¶
ドメインモデルをコードで表現するためのパターン集(ビルディングブロック)。
エンティティ(Entity)¶
属性ではなく**同一性(identity)**によって識別されるオブジェクト。属性が変わっても同じものとして追跡される(例:ユーザー、注文)。
値オブジェクト(Value Object)¶
同一性を持たず、属性の値そのものが意味を持つ不変(immutable)なオブジェクト(例:金額、住所、期間)。プリミティブ型をそのまま使う代わりに値オブジェクトを定義することで、ドメインの概念とバリデーションをコードに表現できる。
集約(Aggregate)¶
整合性を保つ単位としてまとめられたエンティティ・値オブジェクトのかたまり。外部からは集約ルート(root entity)を経由してのみアクセスし、トランザクションの境界も集約単位にする(例:「注文」集約は注文明細を内包し、注文経由でのみ明細を操作する)。
ドメインサービス(Domain Service)¶
特定のエンティティや値オブジェクトに属さないドメインロジックを置く場所(例:口座間の送金処理)。ロジックを何でもサービスに置くとドメインモデル貧血症(anemic domain model)になるため、まずエンティティ・値オブジェクトに置けないか検討する。
リポジトリ(Repository)¶
集約の永続化と再構築を抽象化するインターフェース。ドメイン層からデータベースの詳細を隠蔽する。
ファクトリ(Factory)¶
複雑なオブジェクト(集約)の生成処理をカプセル化する。
ドメインイベント(Domain Event)¶
ドメインで起きた出来事をオブジェクトとして表現する(例:「注文が確定された」)。コンテキスト間の連携やイベントソーシングの基礎となる。Evans本の初版にはなく、後に体系化されたパターン。
アーキテクチャ¶
DDDそのものは特定のアーキテクチャを強制しないが、ドメイン層をUIやデータベースなどの技術的関心事から隔離することが前提となる。
レイヤードアーキテクチャ¶
Evans本で紹介されている構成。上の層は下の層に依存してよいが、逆は不可。
| 層 | 責務 |
|---|---|
| プレゼンテーション層(UI) | ユーザーへの情報表示・入力の解釈 |
| アプリケーション層 | ユースケースの調整役。ビジネスルールは持たない(薄く保つ) |
| ドメイン層 | ビジネスの概念・ルール・状態。DDDの中心 |
| インフラストラクチャ層 | 永続化・メッセージングなどの技術的機能 |
派生アーキテクチャ¶
「ドメインを中心に置き、依存を外側から内側へ向ける」という同じ思想の変種として、以下がよくDDDと組み合わせられる。
オニオンアーキテクチャ(Jeffrey Palermo)
ヘキサゴナルアーキテクチャ/ポート&アダプター(Alistair Cockburn)
クリーンアーキテクチャ(Robert C. Martin)
適用の注意点¶
軽量DDD(DDD-Lite)への批判:エンティティやリポジトリなどの戦術的パターンだけを取り入れ、ユビキタス言語や境界づけられたコンテキストといった戦略的設計を欠いた状態は「軽量DDD」と呼ばれ、DDDの本質(ドメインの理解とモデリング)が抜け落ちているとして批判される
コストに見合うか:ドメインが単純なCRUD中心のアプリケーションでは、DDDのモデリングコストが利益を上回らないことが多い。複雑なビジネスルールを持つコアドメインにこそ適用する価値がある
DDDは特定のアーキテクチャやフレームワークではなく、「ドメインの理解を中心に置く」という考え方。パターンの適用自体が目的化しないよう注意する
Serviceクラス¶
参考¶
Eric Evans (2003) Domain-Driven Design: Tackling Complexity in the Heart of Software(邦訳『エリック・エヴァンスのドメイン駆動設計』翔泳社)
Vaughn Vernon (2013) Implementing Domain-Driven Design(邦訳『実践ドメイン駆動設計』翔泳社)
成瀬允宣 (2020)『ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本』翔泳社
Domain-Driven Design Reference | Eric Evans(Evans本人によるパターンの要約。無料公開)