domain-driven-design について
業務の概念やルールをコードのモデルに反映したいソフトウェアチーム向けのドメイン駆動設計フレームワークです。戦略的設計やモデリングパターンを扱い、診断スコアでモデルを評価できます。
主な機能
- 01ユビキタス言語と境界づけられたコンテキスト
- 02エンティティ、値オブジェクト、集約
- 03ドメインイベントとリポジトリ
- 04ファクトリと仕様パターン
- 05サブドメイン分析とモデル評価
活用シーン
- 01新規システムのドメインモデル定義
- 02複雑なシステムのモデル境界の設定
- 03コード内の業務用語の一貫性確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wondelai/ skills --skill domain-driven-designインストール後、Claude Code で/domain-driven-designを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/bounded-contexts.mdbuilding-blocks.mddomain-events.mdrepositories-factories.mdstrategic-design.mdubiquitous-language.md詳細
ドメイン駆動設計フレームワーク
ビジネスドメインを中心にコードをモデル化し、ソフトウェアの複雑さに対処するためのフレームワークです。ソフトウェアにおける最大のリスクは技術的な失敗ではありません。ビジネスの実態を反映していないモデルを構築することです。
中核となる原則
モデルがコードであり、コードがモデルです。 ソフトウェアには、ビジネスドメインに対する深く共有された理解を体現させる必要があります。ドメインエキスパートと開発者が同じ言語を使い、その言語がコードベースに直接表現されていると、複雑さを管理しやすくなり、ビジネスの変化に合わせてシステムも自然に進化します。
スコアリング
目標:10/10。 クイック診断の各項目(7項目)を満たすごとに1ポイントを加算し、さらに深さに応じて最大3ポイントを加算して、ドメインモデルを評価します。コアドメインに真に豊かなモデルがある(単なる CRUD ではない)場合は+1、サービスではなく集約内に不変条件がある場合は+1、会話・コード・テスト全体でユビキタス言語に一貫性がある場合は+1です。評価区分:9-10=エキスパートにも分かりやすい名前、ACL を伴う明示的なコンテキスト境界、小さな集約、振る舞いが豊かなエンティティ、集約間のフローに使うイベント、特定されたコアドメインがある状態、5-6=ドメイン言語は一部あるものの、境界が漏れている、またはオブジェクトが貧血状態、<=3=技術的な命名、あらゆるものに単一のモデルを使用、ロジックがサービスに散在している状態です。スコアと、満たせていない診断項目を具体的に報告してください。
フレームワーク
1. ユビキタス言語
中核概念: 開発者とドメインエキスパートが共有する、厳密な言語です。会話、ドキュメント、コードで一貫して使用します。言語が変わればコードも変わります。また、コード内の不自然な命名は言語の改善にフィードバックされます。
効果がある理由: 曖昧さは、ほとんどのモデリングの失敗の根本原因です。開発者が「注文」と言い、エキスパートが「購入申請」を意味している場合、バグは避けられません。ユビキタス言語を使えば、コード内のすべての名前を、ビジネスが認識し、妥当性を確認できる概念に対応させることができます。
主な知見:
- 言語は、事後的に用語集を付け足すのではなく、深い協働を通じて生まれます
- 概念に名前を付けにくい場合、モデルが誤っている可能性があります。命名の難しさは設計上のシグナルです
- 技術的な専門用語(
DataProcessorとClaimAdjudicatorの比較)は、修正できる立場にあるエキスパートからドメインロジックを隠してしまいます - 異なる境界づけられたコンテキストで、同じ単語が異なる意味で使われることがあります。それで問題ありません
コードでの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| クラス/メソッドの命名 | ドメインの概念や動詞に基づいて命名します | LoanApplication、policy.underwrite() ― RequestHandler、process() ではありません |
| モジュール構成 | ドメインの概念ごとに整理します | shipping/、billing/ ― controllers/、services/ ではありません |
| コードレビュー | 技術的な名前しか表していない命名は却下します | Manager、Helper、Processor、Utils を命名上の問題として指摘します |
モデリングセッションの実施や用語集の管理については、references/ubiquitous-language.md を参照してください。言語がどのように進化し、コードにフィードバックされるかを説明しています。
2. 境界づけられたコンテキストとコンテキストマッピング
中心となる概念: 境界づけられたコンテキストとは、特定のドメインモデルが適用される明確な境界です。同じ言葉(「顧客」など)でも、コンテキストによって意味が異なる場合があります。コンテキストマップでは、それらの間の関係と変換戦略を定義します。
効果がある理由: 単一の統一モデルを維持しようとする大規模システムは、いずれ必ず不整合に陥ります。境界づけられたコンテキストでは、ビジネスの各部分に異なるモデルが必要であることを受け入れ、コンテキストマップでそれらの統合を管理します。
主な知見:
- 境界づけられたコンテキストはマイクロサービスではありません。複数のサービスを含むこともある、言語とモデルの境界です
- コンテキストの境界はチームの境界と一致することがよくあります(コンウェイの法則)
- 9つのコンテキストマッピングパターンは、チーム間の政治的・技術的な関係を表します
- 腐敗防止層は最も重要な防御パターンです。外部のモデルをコアドメインに決して侵入させないでください
- 共有カーネルは2つのチームを密結合させます。小さく保ち、明示的に管理してください
- まず現状(ビッグボール・オブ・マッド)をマッピングし、その後、目標とする境界を定義します
コードでの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| サービス統合 | 腐敗防止層 | 境界で外部 API のレスポンスを自分のドメインオブジェクトに変換する |
| レガシーシステムの移行 | 追従者/ACL | 自分のドメインの言葉でやり取りするアダプターの背後にレガシーシステムをラップする |
| API 設計 | オープンホストサービス+公開言語 | 標準スキーマを使用した、十分に文書化された REST API を公開する |
9つのマッピングパターンと統合戦略については、references/bounded-contexts.md を参照してください。
3. エンティティ、値オブジェクト、集約
中心となる概念: エンティティは、状態が変わっても存続する識別子を持ちます。値オブジェクトは属性だけで完全に定義され、不変です。集約は、整合性の境界を保証する単一のルートを持つ、エンティティと値オブジェクトのまとまりです。
効果がある理由: これらを区別しないと、すべてが可変で識別子を持つオブジェクトになり、状態が絡み合い、更新に不整合が生じ、並行処理が脆弱になります。集約によって境界が引かれます。内部は常に整合性が保たれ、外部とは結果整合性を前提とします。
主な知見:
- エンティティの判定:「すべての属性が変わっても、私は同じものですか?」(名前や住所が変わっても、その人は同じ人です)
- 値オブジェクトの判定:「私は属性だけで定義されますか?」(10ドル札はどれも別の10ドル札と交換できます)
- ほとんどのものはエンティティではなく値オブジェクトにするべきです。不変性を優先してください
- 集約は小さく保ってください(1つのルートと最小限のまとまり)。他の集約はオブジェクト参照ではなく ID で参照します
- 即時整合性を保証するのは集約の内部だけです。集約間は結果整合性を前提に設計してください
コードでの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| 識別情報の追跡 | ID を持つエンティティ | Order は orderId で識別され、状態が変わっても存続する |
| 不変の属性 | 値オブジェクト | Address(street, city, zip) — 変更ではなく置き換える |
| 整合性の境界 | 集約ルート | Order がルートであり、OrderLine の項目は Order を通じてのみ存在する |
| 並行処理制御 | ルートでの楽観的ロック | Order のバージョンフィールド。2つの編集が競合すると競合が発生する |
集約の設計ルールと整合性の境界については、references/building-blocks.md を参照してください。
4. ドメインイベント
中核となる概念: ドメインイベントは、専門家が関心を持つドメイン内で起きた出来事を記録するもので、過去形の名前(OrderPlaced、PaymentReceived)が付けられます。つまり、すでに発生した事実です。
有効な理由: ドメインイベントによって、原因と結果を切り離せます。OrderPlaced が公開されると、配送、請求、通知がそれぞれ独立して反応します。注文コンテキストはそれらについて把握する必要がありません。これにより、結合が緩和され、結果整合性が実現し、自然な監査証跡が得られます。
主な知見:
- イベントは不変の事実です。一度公開したイベントは変更も撤回もできません
- ドメインイベントは境界づけられたコンテキスト内で使用します。統合イベントは境界を越えます
- イベントによって時間的な疎結合が実現します。プロデューサーはコンシューマーを待ちません
- イベントソーシングでは、イベント履歴全体を信頼できる唯一の情報源として保存し、リプレイによって現在の状態を導き出します
- すべての状態変更にイベントが必要なわけではありません。ドメインが関心を持つものだけを公開します
コードでの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| 状態遷移 | ドメインアクションでイベントを発生させる | order.place() によって OrderPlaced が発生する |
| コンテキスト間の統合 | 統合イベントを公開する | OrderPlaced によって、配送コンテキストで ShippingLabelRequested が発生する |
| 結果整合性 | 非同期イベントハンドラー | OrderPlaced の後、在庫ハンドラーが非同期で在庫を更新する |
参照:references/domain-events.md では、イベントの命名、イベントソーシング、統合イベントについて説明しています。
5. リポジトリとファクトリー
中核となる概念: リポジトリは、永続化を隠蔽し、ドメインオブジェクトのインメモリコレクションのように見せます。ファクトリーは複雑な生成ロジックをカプセル化し、集約が常に有効な状態で生成されるようにします。
有効な理由: 永続化や組み立ての詳細がドメインコードに漏れると、ストレージを変更するたびにビジネスルールに影響が及び、集約が中途半端にしか有効でない状態で構築される可能性があります。リポジトリは SQL/ORM に関する処理をインフラストラクチャに閉じ込めるため、ドメインはメモリ上でテスト可能な状態に保たれます。ファクトリーを使うと、集約の生成経路が不変条件を確実に守るものだけになるため、無効なインスタンスを表現できなくなります。
主な知見:
- リポジトリのインターフェースはドメインレイヤーに、実装はインフラストラクチャに属します
- リポジトリのメソッドにはユビキタス言語を使います。
getByStatusCode(3)ではなく、findPendingOrders()のようにします - コレクション指向のリポジトリでは
add/removeを使い、永続化指向のリポジトリではsaveを使います - 複雑なルールや複数要素の組み立てがある場合はファクトリーが適しています。フィールドが 2 つの値オブジェクトにはコンストラクターだけで十分です
- 仕様パターンは、クエリ条件をドメインオブジェクトとしてカプセル化します。例:
OverdueInvoiceSpecification
コードでの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| データアクセスの抽象化 | リポジトリインターフェース | ドメイン内の OrderRepository.findByCustomer(customerId)、インフラストラクチャ内の PostgresOrderRepository |
| 複雑な生成 | ファクトリーメソッド | Order.createFromQuote(quote) が Quote 集約から注文を検証して組み立てる |
| クエリのカプセル化 | 仕様 | spec = OverdueBy(days=30); repo.findMatching(spec) |
参照:references/repositories-factories.md では、リポジトリ、ファクトリー、仕様の各パターンについて説明しています。
6. 戦略的設計と蒸留
中核となる概念: システムのすべての部分が同じように重要なわけではありません。戦略的設計では、競争優位性が生まれるコアドメインを特定し、サポートサブドメイン(必要ではあるものの、差別化にはつながらないもの)や汎用サブドメイン(コモディティ)と区別します。
効果がある理由: あらゆる領域に同じ厳密さを適用すると、優秀な人材の力が分散し、コモディティ機能を過剰に設計してしまいます。コアドメインを特定することで、最も重要な領域に優秀な開発者と深いモデリングを集中させられます。
主な洞察:
- コアドメイン:最も優秀な人材と深いモデリングを投入します。サポート:構築しますが、過剰に設計しません。汎用(認証、メール、決済):購入するか、オープンソースを利用します
- 蒸留によって、周囲の複雑さからコアドメインを抽出し、際立たせます
- ドメインビジョンステートメントは、コアドメインの価値提案を1ページで説明したものです
- ビジネスの進化に合わせて、何が「コア」なのかを見直します。今日の差別化要因が、明日のコモディティになる可能性があります
コードへの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| 構築するか購入するか | サブドメインの種類を分類する | カスタムの価格設定エンジンを構築する(コア)。決済にはStripeを使う(汎用) |
| チームの配置 | コアドメインに最も優秀な開発者を配置する | シニア開発者が引受ルールをモデリングし、ジュニア開発者がメールサービスを統合する |
| コードの構成 | コアと汎用を分離する | domain/pricing/(深いモデル)とinfrastructure/email/(薄いアダプター) |
エンジニアリングの労力をどこに投入するかを決める際は、references/strategic-design.mdを参照してください。サブドメインの分類と蒸留の手法について説明しています。
よくある間違い
| 間違い | 失敗する理由 | 修正方法 |
|---|---|---|
| ドメイン用語ではなく技術用語を使う | ロジックがDataManagerの背後に隠れ、専門家がモデルを検証できません | ドメイン用語(ClaimAdjudicator)に名前を変更します。ドメイン用語が存在しない場合、その概念自体が誤っている可能性があります |
| すべてを支配する単一のモデル | 請求、配送、マーケティングで単一のCustomerクラスを使うと、肥大化して矛盾が生じます | 境界づけられたコンテキストを設け、それぞれに必要な属性だけを持つ独自のCustomerを用意します |
| 巨大な集約 | 同時実行の競合、読み込みの遅延、トランザクションのボトルネックが発生します | 集約は小さく保ち、IDで参照します。集約間では結果整合性を使用します |
| 貧血ドメインモデル | オブジェクトがデータの入れ物になり、ルールがサービス全体に散らばって重複します | 振る舞いをエンティティと値オブジェクトに移し、サービスはオーケストレーションだけを行います |
| 腐敗防止層がない | 外部のモデルが侵入し、コードが外部スキーマに密結合します | すべての外部システムを変換層の背後にラップします |
| 境界づけられたコンテキスト=マイクロサービス | 早まった切り出しにより、メリットのない分散システムの複雑さが生じます | コンテキストはモデルの境界であり、デプロイ単位ではありません。まずはモノリス内のモジュールから始めます |
| ドメインの専門家を交えない | 開発者が現実に合わないモデルを作り、高額な手戻りが発生します | 専門家が「はい、そのとおりに動きます」と言うまで、定期的にモデリングセッションを行います |
クイック診断
| 質問 | いいえの場合 | 対応 |
|---|---|---|
| ドメインの専門家は、クラス名を読んでその意味を理解できますか? | 技術用語がモデルを覆い隠しています | クラス、メソッド、イベントの名前をユビキタス言語に合わせて変更します |
| 境界づけられたコンテキストの境界は明確に定義されていますか? | モデルが混在し、同じ用語が異なる意味で使われています | コンテキストマップを作成し、境界と翻訳を定義します |
| 集約は小規模ですか(ルート1つと最小限のクラスター)? | 読み込みが遅くなり、並行性の問題が発生します | 集約を分割し、IDで参照し、結果整合性を受け入れます |
| ドメインオブジェクトには、データだけでなく振る舞いも含まれていますか? | 貧血モデルとなり、ロジックがサービスに散在しています | ビジネスルールをエンティティと値オブジェクトに移します |
| 集約間の通信にドメインイベントを使用していますか? | 密結合や同期処理の連鎖が発生します | イベントを導入し、集約が非同期で反応するようにします |
| 外部システムとの連携ごとに腐敗防止層がありますか? | 外部のモデルがドメインを汚染します | 境界ごとに変換レイヤーを追加します |
| どのサブドメインがコアかを特定していますか? | 最も優秀な人材が分散してしまいます | サブドメインを分類し、コアドメインの詳細なモデリングに集中します |
参考資料
手法、パターン、さらに深い洞察については、以下をご覧ください。
著者について
Eric Evans はソフトウェア設計コンサルタントであり、金融、保険、物流分野の大規模システム開発を通じてドメイン駆動設計を提唱しました。2003年に出版された著書 Domain-Driven Design: Tackling Complexity in the Heart of Software は、これまでに書かれたソフトウェアアーキテクチャ書籍の中でも特に影響力のある一冊です。現在も、自身のコンサルティング会社 Domain Language を通じて DDD を発展させ続けています。