DDD の基本概念: ドメインオブジェクト・集約・リポジトリ
前章のワークショップで壁に並べた付箋は、これから扱う用語のどれかに対応します。本章はここから戦術的設計に入り、その用語を定義します。ユビキタス言語と境界づけられたコンテキストは第 5 章が扱っています。
ドメイン駆動設計とは
ドメイン駆動設計(Domain-Driven Design、DDD)は、ビジネスロジックを中心に据えたソフトウェア設計手法です。
DDD は「ビジネス問題」から設計を始め、従来のアプローチは「技術的制約」から始めます。
用語定義
DDD を学ぶ上で重要な用語を定義します。
基本用語
| 用語 | 定義 | 例 |
|---|---|---|
| ドメイン | ソフトウェアが解決しようとするビジネス領域 | ECサイトにおける「注文」「在庫管理」 |
| ドメインオブジェクト | ドメインを表現するオブジェクトの総称 | エンティティと値オブジェクトの2種類 |
| ドメインモデル | ドメインオブジェクトと集約で構成された設計全体 | ドメインを表現する構造の全体像 |
| ビジネスルール | ドメインにおける制約や規則 | 「確定後は商品を追加できない」 |
ドメインオブジェクトの2種類
| 種類 | 識別子 | 同一性の判断基準 | 例 |
|---|---|---|---|
| エンティティ | あり | 識別子で判断 | Order, User, Product |
| 値オブジェクト | なし | 全属性の値で判断 | Money, Address, Email |
値オブジェクト・エンティティ・集約の比較
集約はドメインオブジェクトの種類ではなく境界の概念ですが、対比すると役割の違いが掴みやすいので同じ表に並べます。
| 観点 | 値オブジェクト | エンティティ | 集約 |
|---|---|---|---|
| 定義 | 属性の組み合わせで表現される概念 | 一意の識別子を持つ概念 | 関連するドメインオブジェクトの集まり |
| 識別子 | なし | あり(ID) | 集約ルートがIDを持つ |
| 可変性 | 不変(Immutable) | 可変(Mutable) | 集約ルート経由で変更可能 |
| 同一性判断 | すべての属性値 | 識別子(ID) | 集約ルートのID |
| ライフサイクル | 親オブジェクトに依存 | 集約ルートは独立して存在 集約内部のエンティティは集約ルートに従う | 集約ルートのライフサイクルに従う |
| テーブル表現 | 埋め込み or 値オブジェクトテーブル | 専用テーブル | 複数テーブル(集約境界内) |
具体例で理解する
// 値オブジェクト: 金額
class Money
{
private int $amount;
private string $currency;
public function __construct(int $amount, string $currency)
{
$this->amount = $amount;
$this->currency = $currency;
}
// 不変: 計算結果は新しいインスタンスを返す
public function add(Money $other): Money
{
return new Money($this->amount + $other->amount, $this->currency);
}
// 同一性: すべての属性値で判断
public function equals(Money $other): bool
{
return $this->amount === $other->amount
&& $this->currency === $other->currency;
}
}
// エンティティ: 注文
// 同時に Order集約の集約ルートでもある(OrderとOrderLineをまとめる)
class Order
{
private OrderId $id; // 識別子
private OrderStatus $status;
private Money $total;
private array $orderLines; // 集約内のエンティティ
// 可変: 状態が変わる
public function confirm(): void
{
$this->status = OrderStatus::CONFIRMED;
}
// 同一性: IDで判断
public function equals(Order $other): bool
{
return $this->id->equals($other->id);
}
// 集約ルート経由でのみアクセス
public function addItem(ProductId $productId, int $quantity): void
{
$this->orderLines[] = new OrderLine($productId, $quantity);
}
}
なお、この addItem() は概念を示すための簡略版です。明細に識別子や単価を持たせる形は第 9 章「エンティティ」で扱います。
使い分けの判断基準
判断例:
「金額」
識別子が必要? → NO
→ 値オブジェクト
「注文」
識別子が必要? → YES
独立して存在? → YES
他のオブジェクトと関連? → YES(注文明細と関連)
→ Order集約の集約ルート(注文と注文明細は一緒に管理する)
「注文明細」
識別子が必要? → YES
独立して存在? → NO(注文に依存)
→ Order集約内のエンティティ
集約(Aggregate)
集約はドメインオブジェクトではなく、ドメインオブジェクトをまとめる構造(境界)です。
集約のルール
- 外部からは集約ルート経由でのみアクセス
- 集約ルートが整合性を保証
- 1トランザクションで1集約の更新を推奨(複数集約の更新は結果整合性で対応)
// OK: 集約ルート(Order)経由でアクセス
$order = $orderRepository->findById($orderId);
$order->addItem($productId, $quantity);
// NG: OrderLineに直接アクセス
$orderLine = $orderLineRepository->findById($lineId); // これはNG
リポジトリ(Repository)
リポジトリは、集約の永続化と復元を担当するオブジェクトです。
次章「値オブジェクト」から、各概念を詳しく見ていきます。
参考資料
DDD の基本概念についてさらに学びたい方は、以下の資料をご参照ください。
書籍
- Eric Evans「Domain-Driven Design: Tackling Complexity in the Heart of Software」(2003) - DDD の原典。すべての概念の詳細な説明があります
- 成瀬允宣「ドメイン駆動設計入門」- 日本語で学べる実践的な DDD 入門書。値オブジェクトやエンティティの実装例が豊富です
オンラインリソース
- DDD Reference(Eric Evansの無料PDF) - ユビキタス言語、境界づけられたコンテキスト、集約などの重要概念をまとめたリファレンス
- Martin Fowler氏のブログ - DDD の各概念を簡潔に説明
注記
DDD の概念は最初は難しく感じるかもしれませんが、次章からの実装例でコードを書きながら学べば、徐々に慣れていきます。