Skip to main content

DDD(Domain Driven Design)

ddd1
ddd2

  • 参考になる書類とのこと。 Domain Driven design Quickly
    エリック・エヴァンスのドメイン駆動設計
    現場で役立つシステム設計の原則

DDDを導入する心構え

  • 大事なのは一方向で依存し合っていることだけ。
  • 小さいプロジェクトではオーバースペック気味になる。
  • その場合はCQRSなどの利用を考える

DDD(Domain Driven Design)とは

その前に以下を理解しないといけない

  • ドメインとは ドメインとは専門領域のこと

具体的に言うと

あなたは航空会社に依頼されて、航空監視システムを作ることになりました。 何から手をつければいいでしょうか? 飛行機のクラスを作るところからでしょうか? 各飛行場のDBモデルを作るところからでしょうか? そうではないですね。 最初にやらなければいけないのは、実際にどのように航空を監視しているのか知ることです。

業務に対する十分な知識がないまま、システムを構築することは不可能です。 私たちは飛行機が空を飛んでいること以外何も知らないのです。

ではどのように知ればいいでしょうか。 一体、誰が航空監視システムについて一番詳しいでしょうか? それはもちろん、実際に航空会社で働かれている方です。 専門領域の話は専門家に伺うのが一番確実ですね。

  • モデリング ウォーターフォール設計の功績と問題

ざっくばらんに説明すると

  1. 業務の専門家が要求を書き起こして、それをアナリストへ渡す
  2. アナリストはそれを元に設計書を作り(モデリング)、それがソフトウェア開発者へと流れて行く
  3. 開発者が設計書を元に開発を始める というもの

この手法はソフトウェア設計の伝統的な手法で、一定の成果を上げて長く使われてきました。 しかしいくつかの問題を内包しています。

その中のひとつに 専門家からあなたへと、ドメイン(専門領域)知識が直接渡ってきていないことです。 あなたは、アナリストがモデルに落とし込んだ設計書から、実際にしてほしいことを読みとらなければいけないわけです。

設計書通り作っているうちは問題ないですが、実際のシステムが設計の段階で完璧に収まっていることなどあり得ません。 必ずどこかしらで 「このような状態の時にはどのように振る舞うのが正しいのか」 などの疑問が生まれてきます。

その際、あなたはアナリストへ仕様を伺いに行き、 アナリストは専門家へ何が正しいのかを確認しに行きます。 `まるで伝言ゲームみたいですね。

お互いの関心を共有させるのにもっとも適しているのは、面と向き合って言葉で概念や疑問をぶつけ合うことです。 文字に起こしたり一方通行的な情報フォールだと、知識というのは純度が下がって行きます。 これがウォーターフォール開発が抱えている問題のひとつです。

エリックは上記の問題について 「なら初めから専門家と開発者がミーティングすればよくない?」 と考えました。

これらがDDDの骨幹となっている部分です。

ユビキタス言語とは

開発者と専門家の間では、同じ概念を保持しているが別の名前を持った言葉がいくつも生まれます。

たとえば、漫画ビューワアプリを作成している時に 専門家は「この漫画のこのページにおけるこのコマは」などと話します。 それに対してエンジニアは、聴きながら内面で 「あるcomicに対して、特定のpageでpanelが」なんて思ってるわけです。

つまり、現実のひとつの関心について 専門領域ワード、開発サイドワードと2つの言葉が生まれてしまっているというわけですね。

この様な事態の解決策として、統一した言葉を用意しようとエリックが提唱したのがユビキタス言語です。

レイヤードアーキテクチャ

レイヤードアーキテクチャはDDD本で言及されている、ドメインを隔離するためのアプリケーションアーキテクチャ。 ドメインモデルが設計に忠実に反映されているかを確認するためには、ソフトウェアを構成する要素のうちドメインを表現する責務を持つ部分がどこかを明確にしなければならない。 そこで、レイヤードアーキテクチャでは以下の4層で責務を分離しています。

層の名前責務
ユーザインターフェース/プレゼンテーションユーザに情報を表示して、ユーザのコマンドを解釈する責務を負う。外部アクターは人間のユーザではなく、別のコンピューターシステムのこともある。
アプリケーションソフトウェアが行うことになっている仕事を定義し、表現力豊かなドメインオブジェクトが問題を解決するように導く。(中略) ビジネスルールや知識を含まず、やるべき作業を調整するだけで、実際の処理は、ドメインオブジェクトによって直下のレイヤーで実行される共同作業に委譲する。 (後略)
ドメインビジネスの概念と、ビジネスが置かれた状況に関する情報、およびビジネスルールを表す責務を負う。ビジネスの状況を反映する状態はここで制御され使用されるが、それを格納するという技術的な詳細は、インフラストラクチャに委譲される。この層がビジネスソフトウェアの核心である。
インフラストラクチャ上位のレイヤーを支える一般的な技術的機能を提供する。これには、アプリケーションのためのメッセージ送信、ドメインのための永続化、ユーザインターフェースのためのウィジェット描画などがある。インフラストラクチャ層は、ここで示す4層間における相互作用のパターンも、アーキテクチャフレームワークを通じてサポートすることがある。

DDD ディレクトリ構成参考

参考URL

infrastructuresディレクトリ

DDDのアーキテクチャでは、infrastructuresディレクトリはインフラストラクチャ層を表し、データの永続化や外部APIとの通信などの具体的な技術的詳細を扱います。

インスタンス化について

インスタンス化については、通常、アプリケーション層やインフラストラクチャ層で行います。具体的には、appsディレクトリやinfrastructuresディレクトリ内に、インスタンスの生成や依存関係の注入を行うFactoryクラスやDIコンテナーを配置するのが一般的です

一般的なディレクトリ構造

DDD (Domain-Driven Design) のアーキテクチャにおいては、主に以下の4つの層が考えられます。

  1. ドメイン層(Domain Layer)

    • エンティティ(Entity)
    • 値オブジェクト(Value Object)
    • ドメインサービス(Domain Service)
    • リポジトリ(Repository)
    • アグリゲート(Aggregate)
  2. アプリケーション層(Application Layer)

    • アプリケーションサービス(Application Service)
    • ユースケース(Use Case)
  3. インフラストラクチャ層(Infrastructure Layer)

    • データベースアクセス(Database Access)
    • ネットワーク通信(Network Communication)
  4. インターフェース層(Interface Layer)

    • UI
    • REST API
    • GraphQL API

提供されたディレクトリ構造を見ると、基本的にはDDDの原則に沿った形になっていますが、細かいポイントでいくつか改善の余地があるかもしれません。

  1. アプリケーション層

    • modelsusecases の両方がありますが、モデルはドメイン層に属することが一般的です。アプリケーション層では、アプリケーションサービスやユースケースのクラス/関数を配置します。
  2. ドメイン層

    • entityrepository が正しく配置されていますが、値オブジェクトやドメインサービスもこの層に含めると良いでしょう。
  3. インフラストラクチャ層

    • 各種のインフラストラクチャに関するコードが配置されているようです。ここは特に問題なさそうです。
  4. インターフェース層

    • この層が見当たらないようです。UIやAPIのエンドポイントは、この層に配置します。

改善後のディレクトリ構造の一例は以下のようになります。

core
├── apps
│ └── usecases
├── domain
│ ├── entity
│ ├── repository
│ ├── service // ドメインサービス
│ └── value_object // 値オブジェクト
├── infrastructures
│ ├── cache
│ ├── db
│ ├── dynamo
│ ├── http
│ └── s3
└── interfaces // インターフェイス層
├── api
└── ui

こうした構造にすることで、各層の責務が明確になり、コードの管理や拡張が容易になります。

依存関係について

DDDにおいて、一般的には以下のような依存関係が望まれます。

  1. インターフェース層(Interface Layer)

    • この層は他のどの層にも依存しませんが、アプリケーション層に依存することが一般的です。
  2. アプリケーション層(Application Layer)

    • この層はドメイン層に依存します。インフラストラクチャ層やインターフェース層には直接依存しません。
  3. ドメイン層(Domain Layer)

    • この層は他のどの層にも依存しません。ドメインロジックは純粋であるべきです。
  4. インフラストラクチャ層(Infrastructure Layer)

    • この層は他のすべての層に依存することがあります。具体的な技術やフレームワークを用いて、他の層の抽象を具現化します。

依存関係の方向は、以下のようになります。

Interface Layer
|
v
Application Layer
|
v
Domain Layer
|
v
Infrastructure Layer

しかし、実際のプロジェクトでは、インフラストラクチャ層がドメイン層やアプリケーション層に依存することは避けたいです。このため、依存性の逆転の原則(Dependency Inversion Principle)を利用して、インフラストラクチャ層の実装がドメイン層のインターフェースに依存するようにします。これにより、ドメイン層やアプリケーション層はインフラストラクチャ層の具体的な技術から独立させることができます。

依存性の逆転の原則(Dependency Inversion Principle、DIP)とは

依存性逆転の原則とは、あるモジュールが別のモジュールを利用するとき、モジュールはお互いに直接依存すべきではなく、どちらのモジュールも、共有された抽象(インターフェースや抽象クラスなど)に依存すべきであるという原則です
具体的な実装ではなく、抽象(インターフェースや抽象クラス)に依存することで、高レベルのモジュールと低レベルのモジュールの依存関係を逆転させる考え方

依存性の逆転の原則は、以下の2つのガイドラインから成り立っています:

  1. 高レベルのモジュールは、低レベルのモジュールに依存すべきではない。両方とも抽象に依存すべきである。

    • 高レベルのモジュール:ビジネスロジックやポリシーを含むモジュール。
    • 低レベルのモジュール:具体的な技術や詳細を含むモジュール(データベースアクセス、ネットワーク通信など)。
  2. 抽象は、詳細に依存すべきではない。詳細が抽象に依存すべきである。

    • 抽象:インターフェースや抽象クラス。
    • 詳細:具体的なクラスの実装。

この原則に従うことで、システムの各層(例えば、ドメイン層、インフラストラクチャ層、アプリケーション層)が互いに疎結合になり、コードの再利用性、テスト容易性、メンテナンス性、拡張性が向上します。これは、依存関係の注入(Dependency Injection、DI)やリポジトリパターンなど、様々な設計パターンの基礎となる考え方でもあります。