DDD の全体像 — 戦略的設計と戦術的設計
前章では、ビジネスロジックが Controller と Model に散らばると何が起きるかを見ました。その解決策として DDD を挙げましたが、DDD という言葉が指す範囲は、実は前章で見たものより広いです。
本章は地図です。DDD にどういう層があり、本ガイドがどこを厚く扱うかを先に示します。個々の用語はここでは定義しません。戦略側の語は第 5 章が、戦術側の語は第 7 章以降が定義します。
2 つの設計がある
DDD は 2 つの層からなります。
- 戦略的設計: ドメインをどう分けるか。どこに境界を引き、どのモデルをどの範囲で通用させるか
- 戦術的設計: 境界の内側をどう作るか。値オブジェクト、エンティティ、集約、リポジトリといった構成要素の使い方
Evans の原典は 4 部構成で、第 2 部が「モデル駆動設計の構成要素」、第 4 部が「戦略的設計」です。第 2 部が戦術、第 4 部が戦略にあたります。順序としては戦術が先に来ますが、これは本の構成であって、適用の順序ではありません。
前章で見た Fat Controller の問題は、戦術的設計の道具で解けます。注文の確定条件を Order::confirm() に集めれば、ルールの置き場所は決まります。
ただしこれは「線の内側をどう作るか」の話です。そもそも Order というモデルがどこまでの範囲で通用するのかは、別の問題です。
戦術パターンだけを入れると何が起きるか
戦術パターンは形が分かりやすいので、そこだけを導入しやすい層です。ディレクトリを Domain/ と Application/ に分け、Money クラスを作り、OrderRepositoryInterface を切る。ここまでは機械的に真似できます。
問題は、真似た結果がドメインモデルになっていないことがある点です。
Martin Fowler はこれを Anemic Domain Model (貧血ドメインモデル) と呼んでいます。オブジェクトは存在し、関係も構造も揃っているのに、振る舞いを見るとゲッターとセッターの詰め合わせで、ロジックは全部サービスクラスにある状態です。
データとプロセスを組み合わせるというオブジェクト指向設計の基本的な考え方に反する
— Martin Fowler, AnemicDomainModel
Fowler が指摘する代償は具体的です。ドメインモデルのコストは全部払いながら、利点を何も得られない。O/R マッピングの手間は発生し、クラスの数は増えるのに、結局やっていることはトランザクションスクリプトと同じになります。
// 形だけの値オブジェクト。振る舞いが無く、ただの入れ物になっている
final class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency,
) {}
}
// 計算は全部サービス側にある
final class PriceService
{
public function add(Money $a, Money $b): Money
{
// 通貨が違ったらどうなるか、という判断がここにある
return new Money($a->amount + $b->amount, $a->currency);
}
}
このコードは Money という型を持っていますが、「通貨が違う金額は足せない」というルールが Money の外にあります。同じ判断を別のサービスでも書くことになり、片方だけ直す事故が起きます。
戦術パターンは、ルールをどこに置くかを決める道具です。型を作ることが目的ではありません。この区別は第 8 章以降で実装しながら確かめます。
戦略と戦術の関係
戦略的設計が決めるのは、モデルが通用する範囲です。範囲が決まって初めて、その内側で戦術パターンを使えます。
図の矢印は包含です。1 つの境界づけられたコンテキストの中に複数の集約があり、1 つの集約の中に複数のエンティティと値オブジェクトがあります。
線を引き間違えると、内側をどれだけ丁寧に作っても効きません。「商品」という語が注文・在庫・カタログで別のものを指す場合を次章で見ます。値オブジェクトを導入しても集約を切っても解けません。線の位置が違うからです。
本ガイドがどこを厚く扱うか
| 層 | 扱う章 | 本ガイドでの厚み |
|---|---|---|
| 戦略的設計 | 第 5-6 章 | 境界の引き方と、境界を見つけるワークショップ |
| 戦術的設計 | 第 7-13 章 | 実装コード付き。本ガイドの中心 |
| 層の実装 | 第 14-22 章 | Laravel でどう配置するか |
| 総合実践 | 第 25-26 章 | 注文システムで全層を貫く |
厚みが戦術側に寄っているのは、読者が Laravel で手を動かせる状態を目標にしているためです。戦略的設計は範囲を決める判断で、判断の材料は事業ごとに違うので、コードで示せる部分が少なくなります。
扱わないものもあります。Evans の第 4 部が挙げる「大規模な構造」(責務のレイヤー、進化する秩序) は、複数チームが並行して開発する規模の話なので、本ガイドの想定 (1 つの Laravel アプリケーション) からは外れます。
用語の地図
以降の章で出てくる語を、層ごとに並べます。ここでは定義せず、どの章で扱うかだけを示します。
戦略的設計の語
| 語 | 初出 |
|---|---|
| サブドメイン | 第 5 章 |
| 境界づけられたコンテキスト | 第 5 章 |
| ユビキタス言語 | 第 5 章 |
| コンテキストマップ | 第 5 章 |
| 腐敗防止層 | 第 5 章 |
戦術的設計の語
| 語 | 初出 |
|---|---|
| 値オブジェクト | 第 8 章 |
| エンティティ | 第 9 章 |
| 集約 | 第 10 章 |
| ドメインサービス | 第 11 章 |
| ドメインイベント | 第 12 章 |
| リポジトリ | 第 17 章 |
どこから始めるか
DDD を既存のプロジェクトに入れるとき、戦略から始めるか戦術から始めるかは、状況で変わります。
- 境界が既に明確なら戦術から。1 つの業務を 1 つのアプリケーションで扱っていて、モデルの意味が場面で変わらないなら、値オブジェクトの導入から始められます
- 同じ語が場面で違う意味を持つなら戦略から。「商品」「ユーザー」「案件」のような語について、部署ごとに違うものを指していると分かっているなら、先に線を引きます
判断がつかないときは、次章のワークショップで業務の流れを並べると見えてきます。業務の担当者が「それは別の話」と言って区切る場所が、だいたい境界の候補になります。
まとめ
| ポイント | 内容 |
|---|---|
| 2 つの層 | 戦略 = どこに線を引くか / 戦術 = 内側をどう作るか |
| 順序 | 線が決まらないと内側は作れない。ただし境界が自明なら戦術から入れる |
| 形だけの導入 | 型を作ってもルールが外にあれば、コストだけ払って利点が無い |
| 本ガイドの厚み | 戦術と実装が中心。戦略は第 5-6 章で範囲の決め方を扱う |
次章では、線をどう引くかを具体的に見ていきます。
参考資料
- Eric Evans『エリック・エヴァンスのドメイン駆動設計』(翔泳社) — 第 2 部が戦術的設計、第 4 部が戦略的設計にあたります
- Martin Fowler「AnemicDomainModel」 — 戦術パターンを形だけ導入した状態への批判
- Martin Fowler「BoundedContext」 — 境界づけられたコンテキストの概要