どすえのブログ

ソフトウェア開発ブログ

ドメイン駆動設計(DDD)入門

目次:

  1. はじめに: ドメイン駆動設計とは
  2. ドメイン駆動設計の要素
  3. 戦術的設計パターン
  4. 戦略的設計: ドメイン駆動設計のコンテキストマッピング
  5. ドメイン駆動設計の実践
  6. ドメイン駆動設計とアーキテクチャ
  7. 結論: ドメイン駆動設計の採用を検討する

1. はじめに: ドメイン駆動設計とは

ドメイン駆動設計 (Domain-Driven Design, DDD) は、ソフトウェア開発プロセスにおいて、ビジネスドメイン(業務知識や業務要件)を中心に据え、それを表現するソフトウェア設計手法の一つです。ドメイン駆動設計は、エリック・エヴァンスによって開発されました。

この設計手法は、ビジネス要件が複雑で変更が多いプロジェクトに特に適しており、開発チームがドメインエキスパートと緊密に協力し、モデル駆動設計を通じて効果的なソフトウェアを実現することを目指します。

1.1. ドメイン駆動設計の基本概念

ドメイン駆動設計は、いくつかの基本概念に基づいています。これらの概念は、開発プロセスを通じてビジネスドメインを理解し、表現するための指針を提供します。

  • ユビキタス言語 (Ubiquitous Language): プロジェクト全体で共有される言語や用語。ドメインエキスパートと開発チームが同じ言語を使用することで、誤解を減らし、効果的なコミュニケーションを促進します。
  • 境界付けられたコンテキスト (Bounded Context): システム内の特定の部分において、モデルや言語が一貫性を持つ範囲。これにより、モデルの整合性を保ちながら、システム全体の複雑性を管理します。
  • エンティティ (Entity) と値オブジェクト (Value Object): ドメインモデル内のオブジェクトを表現するための手法。エンティティは一意の識別子を持ち、値オブジェクトは属性によって特徴付けられます。
  • 集約 (Aggregate): ドメインモデル内のオブジェクトのまとまりで、一貫性のあるユニットとして扱われます。集約は、データの整合性を保ちながら、システムのスケーラビリティとパフォーマンスを向上させる役割を果たします。

  • ドメインイベント (Domain Event): ビジネスドメインにおいて重要な出来事や状態の変化を表現します。イベント駆動アーキテクチャと組み合わせることで、システム全体の柔軟性と可用性を高めることができます。

1.2. ドメイン駆動設計の目的とメリット

ドメイン駆動設計の主な目的は、ビジネスドメインの知識をソフトウェア設計に取り込み、効果的なソフトウェアを実現することです。以下に、ドメイン駆動設計を採用することで得られる主なメリットを挙げます。

  • ドメインエキスパートと開発チームのコミュニケーションが向上し、誤解が減ります。ユビキタス言語を用いることで、チーム全体が共通の言語を使用し、効果的なコミュニケーションが可能になります。
  • 複雑なビジネス要件を効果的にモデル化できます。境界付けられたコンテキストや集約などの概念を用いることで、システム全体の複雑性を管理しながら、ビジネスロジックを適切に表現することができます。
  • ソフトウェアのメンテナンス性が向上します。ドメイン駆動設計では、ドメインモデルが中心となります。これにより、コードの構造が明確になり、変更や拡張が容易になります。
  • 開発チームがビジネスドメインの知識を深めることができます。ドメイン駆動設計では、開発チームがドメインエキスパートと緊密に協力し、ビジネスの知識を取り込むことが重要です。これにより、開発チームがビジネスドメインに精通し、より効果的なソフトウェアを開発できるようになります。

以上のように、ドメイン駆動設計はビジネスドメインを中心に据えたソフトウェア設計手法であり、複雑なビジネス要件を効果的にモデル化し、開発チームとドメインエキスパート間のコミュニケーションを強化することを目指します。これにより、以下のような追加のメリットが期待できます。

  • 開発プロセスが効率化:ドメイン駆動設計では、ドメインエキスパートと開発チームが密接に連携することで、要件定義や設計の段階で誤解や不明確な点を解消することができます。これにより、開発プロセス全体がスムーズに進み、時間とコストの削減が図られます。
  • ソフトウェア品質の向上:ドメイン駆動設計では、ビジネスドメインの知識をソフトウェア設計に適切に反映することで、ビジネス要件を正確に満たすソフトウェアを実現することができます。また、ドメイン駆動設計の概念やパターンを適用することで、ソフトウェアの構造が整理され、安定性や可用性が向上します。
  • ビジネス価値の高いソフトウェアの実現:開発チームがビジネスドメインに精通し、ビジネス要件を適切に理解することで、ビジネス価値の高い機能や改善点を見つけやすくなります。これにより、組織全体としての競争力が向上し、市場での成功が期待できます。

2. ドメイン駆動設計の要素

ドメイン駆動設計 (DDD) は、ソフトウェア開発プロセスの中心にドメインの概念を置くことを目指しています。以下では、DDD の主要な要素について説明します。

2.1. ユビキタス言語

ユビキタス言語とは、プロジェクトチーム内で共有される共通の言語や用語のことです。

開発者、プロダクトオーナー、ドメインエキスパートなど、チーム内の異なる役割を持つメンバーが、ドメインの概念やビジネスルールについて明確に理解し、適切にコミュニケーションできるようにするために必要です。ユビキタス言語は、コードの中にも反映され、ドメインに関連するオブジェクトや操作が具体的な名前で表現されます。

2.2. 境界付けられたコンテキスト

境界付けられたコンテキストは、ドメインモデルを適用する範囲や、それらが適用されるコンテキストを明確に定義することです。

これにより、モデルの一貫性を維持しつつ、複雑なドメインを分割して管理することができます。コンテキストの境界は、モデル間の相互作用や、異なるコンテキストでのモデルの変換方法を明確にします。

2.3. エンティティと値オブジェクト

エンティティは、独自の識別子を持ち、そのライフサイクルに沿って状態が変化するオブジェクトです。

識別子によって同一性が判断されるため、属性が同じであっても、識別子が異なれば異なるエンティティとみなされます。

一方、値オブジェクトは、状態が変化しない不変のオブジェクトで、属性によって同一性が判断されます。

エンティティと値オブジェクトを適切に使い分けることで、ドメインモデルの整合性を保ちながら、複雑さを抑えることができます。

2.4. 集約

集約は、ドメインモデル内の関連するオブジェクトのグループです。

集約は、集約は、一つのルートエンティティを持ち、それを通じて他のオブジェクトにアクセスします。ルートエンティティは、集約内の他のオブジェクトに対する操作を調整し、ビジネスルールを適用します。集約は、整合性の境界を明確にし、トランザクションを制御することができます。

集約の設計原則には、以下の3点が挙げられます。

  1. 集約内のオブジェクトは、ルートエンティティを介してアクセスされること。
  2. 集約の境界に沿って、整合性を保つためのトランザクション制御が行われること。
  3. 集約が他の集約への参照を持つ場合、その参照はIDによって行われること。

これらの原則に従うことで、システム全体の整合性が向上し、複雑さが抑えられます。

2.5. ドメインイベント

ドメインイベントは、ドメイン内で重要な状態の変化を表すもので、システムが反応すべき事象を定義します。

ドメインイベントは、通常、名詞+動詞の形式で表現され、過去形で記述されることが多いです(例:注文が確定された、商品が出荷されたなど)。

ドメインイベントを利用することで、システム間の結合を緩和し、拡張性や保守性を向上させることができます。また、イベント駆動アーキテクチャや、マイクロサービスといったアーキテクチャパターンとの相性が良いため、DDDと組み合わせることが多いです。

3. 戦術的設計パターン

ドメイン駆動設計(DDD)における戦術的設計パターンは、ドメインオブジェクトの振る舞いと構造を実現するために使用される一連のパターンです。これらのパターンは、コードの品質を向上させるだけでなく、ドメインの知識を効果的に表現するのに役立ちます。ここでは、主要な戦術的設計パターンについて説明します。

3.1. リポジトリ

リポジトリは、ドメインオブジェクト(主にエンティティ)の永続化と復元を担当するパターンです。

リポジトリは、ドメインオブジェクトとデータストア(データベースや外部サービス)との間の抽象化レイヤーを提供し、ドメインロジックがデータストアの詳細を気にすることなく、オブジェクトの操作に集中できるようにします。

3.2. ファクトリー

ファクトリーは、ドメインオブジェクトの生成と構築を担当するパターンです。

オブジェクトの生成が複雑であったり、特定の状態で初期化する必要がある場合、ファクトリーを使用してオブジェクトの生成ロジックをカプセル化することができます。これにより、コードの可読性が向上し、オブジェクトの生成に関する知識が一元化されます。

3.3. サービス

サービスは、ドメインオブジェクトの振る舞いを実現するパターンで、特定のエンティティや値オブジェクトに属さないドメインロジックをカプセル化します。

サービスは、オブジェクト間の協調やデータ変換など、複数のドメインオブジェクトをまたがる操作を実行する場合に特に役立ちます。

3.4. アプリケーションサービスとドメインサービス

アプリケーションサービスは、ドメインオブジェクトを操作するためのAPIを提供し、ドメインサービスと連携してユースケースを実現します。

アプリケーションサービスは、トランザクション管理やセキュリティなどのインフラストラクチャに関する関連事項を扱い、ドメインサービスとの橋渡し役を果たします。一方、ドメインサービスは、ドメインのビジネスロジックに焦点を当て、状態を持たないサービスとして実装されます。これにより、ドメインロジックが明確に表現され、ビジネスルールが適切に実装されます。

アプリケーションサービスとドメインサービスの違いは、責任範囲と対象となるロジックです。アプリケーションサービスは、ドメインのロジックに直接関与せず、ユースケースを実行するためのプロセスを調整します。一方、ドメインサービスは、ドメインモデルに関連するビジネスロジックを実装し、ドメインオブジェクトが持つべきではないロジックをカプセル化します。

4. 戦略的設計: ドメイン駆動設計のコンテキストマッピング

戦略的設計は、ドメイン駆動設計のプロセスにおいて、システム全体の構造と相互作用を考慮する段階です。コンテキストマッピングは、システム内の異なるドメインやサブドメインがどのように関連し、互いに影響を与えるかを理解するための手法です。以下に、戦略的設計における主要なパターンを紹介します。

4.1. 共有カーネル

共有カーネルは、複数の境界付けられたコンテキスト間で共有されるドメインモデルの一部を指します。これにより、チームは重要な概念やロジックを一元化し、重複や矛盾を避けることができます。共有カーネルは、慎重に管理され、変更には全チームの合意が必要です。

4.2. カスタマーサプライヤー

カスタマーサプライヤーは、1つの境界付けられたコンテキストが他の境界付けられたコンテキストのニーズに応える関係を表します。カスタマー側のチームは、サプライヤー側のチームが提供する機能やデータに依存します。このパターンは、依存関係を明確にし、両チーム間での協力を促進します。

4.3. アンチコラプションレイヤー

アンチコラプションレイヤーは、境界付けられたコンテキスト間の通信を独立させる層です。これにより、1つのコンテキストの変更が他のコンテキストに悪影響を与えるのを防ぎます。アンチコラプションレイヤーは、データ変換やアダプターパターンを用いて、異なるドメインモデル間での情報のやりとりを可能にします。

4.4. オープンホストサービス

オープンホストサービスは、境界付けられたコンテキストが、他のコンテキストや外部システムと通信するために公開するAPIやプロトコルです。これにより、システムの拡張性や統合性が向上します。オープンホストサービスは、標準化されたインターフェースと良好なドキュメントを提供することが重要であり、他のチームやシステムとの連携を容易にします。

4.5. 発行者/購読者

発行者/購読者パターンは、境界付けられたコンテキスト間で非同期通信を行う方法です。このパターンでは、1つのコンテキスト(発行者)がドメインイベントを生成し、他の関心のあるコンテキスト(購読者)がそのイベントを受け取り、処理します。発行者と購読者は、疎結合であるため、互いに影響を与えることなく進化・拡張が可能です。

発行者/購読者パターンは、マイクロサービスアーキテクチャやイベント駆動アーキテクチャにおいて特に有効であり、システム全体のスケーラビリティや柔軟性を向上させます。

これらの戦略的設計パターンを適切に適用することで、ドメイン駆動設計はシステムの構造や相互作用を整理し、チーム間の協力やコミュニケーションを促進します。結果として、プロジェクトはより効率的に進められ、システムの品質やメンテナンス性が向上することが期待されます。

5. ドメイン駆動設計の実践

5.1. ドメイン駆動設計の導入方法

ドメイン駆動設計(DDD)を導入するためには、以下の手順が推奨されます。

  1. ドメインエキスパートと協力して、対象となるドメインを理解する。ユビキタス言語を作成し、チーム全員が共通の言語を使用することを確認する。
  2. 境界付けられたコンテキストを特定し、サブドメイン間の関係を明確化する。コンテキストマッピングを使用して、サブドメイン間の相互作用を視覚化する。
  3. エンティティ、値オブジェクト、集約、ドメインイベントを特定し、ドメインモデルを設計する。
  4. リポジトリ、ファクトリー、サービスなどの戦術的設計パターンを適用して、モデルの永続化やビジネスロジックの実装を行う。
  5. イテレーティブな開発プロセスを適用し、フィードバックを受け入れてドメインモデルを改善する。

5.2. ドメイン駆動設計におけるチームの役割

DDDにおけるチームの役割は、以下のようになります。

  1. ドメインエキスパート: ビジネスの専門知識を持ち、開発チームと協力してドメインモデルを構築します。
  2. ソフトウェア開発者: ドメインモデルをもとに、ソフトウェアの設計と実装を行います。開発者は、ユビキタス言語を使用してドメインエキスパートとコミュニケーションします。
  3. アーキテクト: システム全体のアーキテクチャを設計し、技術選定や戦略的設計の決定を行います。

5.3. ドメイン駆動設計プロジェクトの進め方

DDDプロジェクトを進める際には、以下のポイントに注意してください。

  1. イテレーティブな開発: DDDは、ドメインの知識が不完全な状態から始まることが一般的です。そのため、継続的にフィードバックを受け入れてドメインモデルを改善することが重要です。短期的なリリースサイクルを使用して、頻繁に機能をリリースし、ドメインエキスパートとのコラボレーションを維持します。

  2. ユビキタス言語の維持: プロジェクト全体でユビキタス言語を維持することが重要です。チーム内で用語や概念に関する矛盾があれば、それを解決し、言語を統一します。ドキュメントやコード内での用語の整合性も保ちます。

  3. コンテキストマッピングの活用: コンテキストマッピングを活用して、サブドメイン間の相互作用や依存関係を明確にし、それに基づいて戦略的設計を行います。

  4. ドメインモデルの継続的なリファクタリング: ドメインモデルは、ドメイン知識が進化するにつれて変更されるべきです。継続的なリファクタリングを行い、ドメインモデルを最新の状態に保ちます。

  5. チームの教育とスキル向上: DDDの概念やパターンに習熟することが、プロジェクトの成功につながります。チームメンバーにDDDに関する教育やトレーニングを提供し、スキルを向上させることが重要です。

  6. 適切なアーキテクチャの選択: DDDプロジェクトでは、ヘキサゴナルアーキテクチャやクリーンアーキテクチャなど、適切なアーキテクチャを選択して適用することが重要です。これにより、ドメインロジックと技術的な詳細が分離され、システムの柔軟性が向上します。

総合的に、ドメイン駆動設計プロジェクトの進め方は、イテレーティブな開発、ユビキタス言語の維持、コンテキストマッピングの活用、ドメインモデルの継続的なリファクタリング、チームの教育とスキル向上、そして適切なアーキテクチャの選択を意識して進めることが求められます。これらのポイントに注意しながら、プロジェクトを適切に推進し、成功に導くことができます。

  1. モデリングワークショップの実施: チームとドメインエキスパートとの間で、定期的にモデリングワークショップを開催することが効果的です。これにより、ドメインモデルの改善や新たな知見の発見、チーム内での共通理解の構築が容易になります。

  2. テスト駆動開発の適用: テスト駆動開発(TDD)をDDDプロジェクトに適用することで、ドメインモデルの品質向上やリファクタリングの容易さが向上します。TDDは、開発者がドメインモデルに関する仕様を理解し、適切な実装を行うのに役立ちます。

  3. パフォーマンスやスケーラビリティへの配慮: DDDプロジェクトでは、ドメインモデルの複雑性が増すことがあります。そのため、パフォーマンスやスケーラビリティへの配慮が重要です。アーキテクチャや設計の段階で、パフォーマンスやスケーラビリティに関する要件を明確にし、適切な戦略を選択して実装します。

  4. 成果物のドキュメンテーション: ドメインモデルやアーキテクチャ、設計パターンなど、プロジェクトの成果物に関するドキュメンテーションを整備することが重要です。これにより、チーム内での知識共有が容易になり、プロジェクトの継続性が向上します。

これらのポイントを遵守することで、ドメイン駆動設計プロジェクトは、スムーズかつ効果的に進行し、成功に導くことができます。チーム全体がDDDの概念や手法に精通し、継続的なコミュニケーションと協力を保ちながらプロジェクトを推進することが、最終的な成功の鍵となります。

6. ドメイン駆動設計とアーキテクチャ

ドメイン駆動設計 (DDD) は、ソフトウェア開発のアプローチであり、特定のアーキテクチャスタイルに依存していません。しかし、DDD は一部のアーキテクチャパターンと非常に相性が良く、その実践を容易にします。この章では、ヘキサゴナルアーキテクチャ、クリーンアーキテクチャ、およびマイクロサービスといったアーキテクチャパターンと DDD の関係について解説します。

6.1. ヘキサゴナルアーキテクチャ

ヘキサゴナルアーキテクチャ(ポート・アンド・アダプターズアーキテクチャとも呼ばれます)は、アプリケーションの内部と外部の関心事を明確に分離するアーキテクチャパターンです。このアーキテクチャは、アプリケーションのドメインロジックを中心に配置し、周囲にポートとアダプターを設けることで、外部システムとの連携や技術的詳細を疎結合に保ちます。

DDD とヘキサゴナルアーキテクチャは、ドメインロジックに焦点を当てる点で一致しています。境界付けられたコンテキストを用いることで、ヘキサゴナルアーキテクチャは DDD の概念を適切に表現できる柔軟性を提供します。

6.2. クリーンアーキテクチャ

クリーンアーキテクチャは、ソフトウェアの構造を明確にする目的で提案されたアーキテクチャパターンです。このアーキテクチャでは、アプリケーションの各要素が特定の責任を持ち、依存関係が内側から外側へ向かうように設計されています。

ドメイン駆動設計とクリーンアーキテクチャは、ドメインロジックを中心に据え、技術的な詳細を取り巻く構造を整理する点で相性が良いです。DDD の戦術的パターン(エンティティ、値オブジェクト、集約、リポジトリなど)は、クリーンアーキテクチャの各層に自然に適用されます。例えば、エンティティや値オブジェクトはドメイン層に配置され、リポジトリやドメインサービスはインフラストラクチャ層やアプリケーション層に配置されます。

クリーンアーキテクチャを用いることで、ドメイン駆動設計の原則に基づいて、様々な技術やフレームワークと疎結合に連携するソフトウェアを実現できます。これにより、アプリケーションの保守性や拡張性が向上し、プロジェクトの寿命が延びることが期待できます。

6.3. ドメイン駆動設計とマイクロサービス

マイクロサービスアーキテクチャは、大規模なアプリケーションを小さなサービスに分割して開発・運用するアプローチです。各サービスは独立してデプロイ・スケールでき、チーム間のコミュニケーションや開発速度の向上を目指します。

ドメイン駆動設計とマイクロサービスアーキテクチャは、互いに補完し合う関係にあります。DDD の境界付けられたコンテキストは、マイクロサービスの分割単位を決定する際の指針となります。一方で、マイクロサービスアーキテクチャは、DDD の戦略的設計を実現する上で有用なパターンを提供します。

具体的には、各マイクロサービスが独自のユビキタス言語とドメインモデルを持ち、他のサービスとの連携は API やイベントを介して行われます。これにより、各サービスのドメインロジックが明確に分離され、全体として疎結合なシステムが構築できます。

また、マイクロサービスアーキテクチャを採用することで、DDD の戦略的設計パターン(共有カーネル、カスタマーサプライヤー、アンチコルラプションレイヤなど)が実践しやすくなります。これらのパターンは、サービス間の依存関係を適切に管理し、ドメインの知識を共有する上で役立ちます。

これらのアーキテクチャパターンは、ドメインロジックを中心に据え、技術的な詳細や外部システムとの連携を疎結合に保つことを重視しています。その結果、アプリケーションの保守性や拡張性が向上し、チームの生産性が高まることが期待できます。

7. まとめ: ドメイン駆動設計の採用を検討する

このブログでは、ドメイン駆動設計(DDD)の基本概念や要素、戦術的・戦略的設計、アーキテクチャについて説明しました。DDDは、以下のようなプロジェクトに適しています。

  1. 複雑なドメインロジックを持つプロジェクト: DDDは、ビジネスルールやドメイン知識をコード上で表現しやすくすることを目指しています。そのため、複雑なビジネスロジックを持つプロジェクトには特に効果的です。

  2. 長期的なプロジェクト: DDDは、ソフトウェアの保守性や拡張性を向上させることを目指しています。長期にわたって開発や保守が行われるプロジェクトには、DDDのアプローチが有効です。

  3. チームがビジネスエキスパートと緊密に協力するプロジェクト: DDDは、チームがビジネスエキスパートと共同でドメイン知識を明確化し、ソフトウェアに反映することを重視しています。このような協力が実現できるプロジェクトでは、DDDの効果が最大化されます。

総じて、ドメイン駆動設計は、複雑なドメインロジックを持つプロジェクトや長期的なプロジェクトにおいて、開発効率や保守性の向上に寄与するアプローチです。ただし、その導入には学習コストやドメイン知識の不足などの課題が伴います。これらの課題に対処しながら、適切なプロジェクトでDDDを採用することで、そのメリットを最大限に活かすことができるでしょう。