メインコンテンツまでスキップ

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. 外部からは集約ルート経由でのみアクセス
  2. 集約ルートが整合性を保証
  3. 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 の概念は最初は難しく感じるかもしれませんが、次章からの実装例でコードを書きながら学べば、徐々に慣れていきます。