実装中の問いから引く

DDD実装ガイドライン

Entity、Value Object、Aggregate、Repositoryなど、DDDの実装中に迷いやすい判断を、推奨・判断基準・例外・Good / Bad Exampleで確認できます。

12の判断ルール試行版

このガイドラインの使い方

上から順に学ぶ章立てではありません。いま迷っている問いを目次から選び、まず「推奨」と「判断基準」を確認してください。例外は、推奨から外れる根拠をチームで説明するときに使います。

Rule 01

Entityはいつ作る?

推奨

属性ではなく同一性で追跡し、時間とともに状態が変わる対象をEntityにする。

なぜ

値が変わっても同じ対象として扱う必要がある場合、識別子とライフサイクルをモデルに明示する必要があります。

判断基準

  • 履歴や状態遷移を追う
  • 同じ属性でも別物として区別する
  • 業務上の識別子が会話に現れる

BAD

表示用の都道府県一覧の各項目に、将来使うかもしれないという理由だけでEntity IDを付ける。

GOOD

注文は配送先が変わっても同じ注文IDの注文として追跡する。

Rule 02

Value Objectにすべき基準は?

推奨

値の組み合わせで意味と等価性が決まり、独立したライフサイクルを持たない概念をValue Objectにする。

なぜ

検証と振る舞いを値の型へ閉じ込めると、不正な値や単位の取り違えを境界で防げます。

判断基準

  • 構成する値が同じなら交換可能
  • 生成後は不変にできる
  • 金額・期間・住所など固有のルールがある

BAD

価格をnumber、通貨をstringとして別々に引き回し、各呼び出し元で検証する。

GOOD

Moneyが金額と通貨を保持し、異なる通貨同士の加算を拒否する。

Rule 03

Aggregate Rootとは何か?

推奨

Aggregateの不変条件を守る唯一の入口となるEntityをAggregate Rootにする。

なぜ

外部からの変更経路を一つに絞ることで、内部の複数オブジェクトにまたがるルールを常に検証できます。

判断基準

  • 外部からIDで参照される
  • 内部Entityの生成・変更を調整する
  • トランザクション整合性の境界になる

BAD

呼び出し側がorder.linesへ直接pushし、検証を迂回できる。

GOOD

Order.addLine()が重複商品や数量上限を検証してOrderLineを追加する。

Rule 04

Aggregateの外から内部Entityを変更していい?

推奨

内部Entityの変更はAggregate Rootが公開する意図のある操作を経由する。

なぜ

内部Entityだけを見て変更すると、Aggregate全体で守るべき不変条件が破られる可能性があります。

判断基準

  • setterではなく業務上の操作名を使う
  • 変更前にAggregate全体の条件を検証する
  • 内部コレクションを可変で公開しない

BAD

order.getLines()[0].quantity = 100 のように内部状態を直接書き換える。

GOOD

order.changeQuantity(lineId, quantity)で対象行と注文上限を同時に検証する。

Rule 05

RepositoryはAggregateごとに作る?

推奨

永続化が必要なAggregate RootごとにRepositoryを用意し、内部Entity用のRepositoryは作らない。

なぜ

取得と保存の単位を整合性境界と揃えると、Aggregateを部分的に更新して不変条件を壊す経路を減らせます。

判断基準

  • 保存対象はAggregate Root
  • ドメインで必要な取得操作だけを定義する
  • テーブル単位のCRUDにしない

BAD

すべてのDBテーブルに同じCRUD Repositoryを機械的に生成する。

GOOD

OrderRepositoryがOrderを取得・保存し、OrderLineRepositoryは作らない。

Rule 06

Repository interfaceはどこに置く?

推奨

Repositoryの抽象は利用側であるドメインまたはアプリケーション側に置き、DB実装をInfrastructure側に置く。

なぜ

ドメインのユースケースがデータベースやORMへ依存せず、必要な取得能力を自分の言葉で定義できます。

判断基準

  • メソッド名がドメインの語彙になっている
  • ORM型を公開しない
  • 実装詳細への依存が内向きに入り込まない

BAD

domainがORMのBaseRepositoryやQueryBuilderを直接importする。

GOOD

applicationのPlaceOrderが要求するOrderRepositoryをdomain側で定義し、SqlOrderRepositoryが実装する。

Rule 07

Domain Serviceはいつ使う?

推奨

重要なドメイン操作が一つのEntityやValue Objectに自然に属さない場合に限って使う。

なぜ

無理に一つのEntityへ置くより意味が明確になりますが、使いすぎるとデータと振る舞いが分離した貧血モデルになります。

判断基準

  • 複数のドメインオブジェクトが関与する
  • 操作自体がドメインの語彙である
  • アプリケーションの手順やI/Oではない

BAD

Orderの全ロジックをOrderServiceへ移し、Orderをgetter/setterだけにする。

GOOD

複数口座間の振替可否を判定するTransferPolicy。

Rule 08

Application Serviceには何を書く?

推奨

ユースケースの進行、入出力、認可、Repository呼び出し、Transaction境界を調整し、業務判断はDomain Modelへ委譲する。

なぜ

アプリケーションの手順とドメインルールを分けると、同じルールを別の入口から安全に再利用できます。

判断基準

  • 処理の順序を組み立てる
  • 外部I/Oを調整する
  • ドメインオブジェクトの操作を呼び出す

BAD

Application Service内のif文だけでキャンセル期限や状態遷移を判定する。

GOOD

注文を取得し、order.cancel(reason)を呼び、保存してイベントを公開する。

Rule 09

Domain ModelとDB Modelは分ける?

推奨

ORM制約がドメイン設計を歪める場合は分ける。単純な領域では同一モデルから始めてもよい。

なぜ

分離には変換コストがある一方、複雑なモデルをテーブル構造や遅延読み込みから守れます。

判断基準

  • ORM注釈や継承制約がモデルへ漏れている
  • 保存形式とドメインの形が大きく異なる
  • DBなしでドメインテストを行いたい

BAD

常に分離するという理由だけで、単純なCRUDにも大量の同形クラスと変換コードを作る。

GOOD

Repository内のMapperがOrderRecordとOrder Aggregateを相互変換する。

Rule 10

IDはDomain側とDB側のどちらで生成する?

推奨

生成直後から識別が必要ならDomain側で生成する。DBの連番は永続化後までID不要な場合に限定する。

なぜ

保存前にIDがあれば、Domain Eventや他オブジェクトの参照をDBへの依存なしで組み立てられます。

判断基準

  • 保存前にイベントへIDを含める
  • 分散環境で衝突なく生成する
  • ID形式自体に業務ルールがある

BAD

仮の0やnullをIDとして持たせ、保存後にDomain Modelを不完全な状態から修復する。

GOOD

FactoryがOrderIdを生成し、完全なOrderをRepositoryへ渡す。

Rule 11

例外クラスはどこに置く?

推奨

破られたルールの所有者と同じ層に置き、技術例外は境界でアプリケーション向けの失敗へ変換する。

なぜ

ドメインの失敗と通信・DBの失敗を区別すると、呼び出し側が意味に応じた処理を選べます。

判断基準

  • ドメイン例外は業務語彙で命名する
  • ORM例外をUIまで漏らさない
  • 予期できる不成立はResult型も検討する

BAD

すべてをBusinessException一種類に包み、原因や回復方法を失う。

GOOD

OrderAlreadyShippedをdomainに置き、DB接続失敗はinfrastructureで変換する。

Rule 12

Transactionはどこで開始する?

推奨

原則としてApplication Serviceの1ユースケースを境界に開始し、1つのAggregateを原子的に更新する。

なぜ

ユースケースの成功・失敗とコミットを揃えつつ、ドメインをDBのTransaction APIから独立させられます。

判断基準

  • Application ServiceまたはUnit of Workが制御する
  • Domain ModelはTransaction APIを知らない
  • 複数Aggregateの長いロックを避ける

BAD

Entityのメソッド内でDB Transactionを開始し、外部API呼び出し中もロックを保持する。

GOOD

PlaceOrderの開始時にTransactionを開き、Order保存とOutbox記録を同時にコミットする。

この内容を書くときに確認した情報

原典、著者による説明、実践者の資料を分けて確認しています。

  1. Eric Evans · original-reference

    DDD Reference — Definitions and Pattern Summaries

    最終確認: 2026-08-08

  2. Martin Fowler · practitioner-article

    Domain Driven Design

    最終確認: 2026-08-08