DDD(ドメイン駆動設計)の概要をまとめてみた
はじめに
エンジニア歴1年未満の新米です!
最近、DDDを用いた開発に携わることになったため、アウトプットを兼ねてこちらの記事にまとめていきたいと思います!
DDDってなんぞや?って方が、この記事を読んで少しでも理解していただけたら幸いです・・・!
DDDとは
Domain-Driven Design(ドメイン駆動設計)は、エリック・エヴァンスが開発したソフトウェア開発手法の一つです。複雑なビジネスドメインや問題領域を解決するために、そのドメインに焦点を当てる方法論となります。ちなみに、DDDにおいてのドメインは「ソフトウェアで問題解決しようとする対象領域」のことを表します。
エリック・エヴァンスのドメイン駆動設計の内容をリファレンスとしてまとめたものである「DDD Reference」には、以下のように書かれてあります。
「Many projects do modeling work without getting much real benefit in the end. The patterns of DDD distill successful practices from projects where dramatic benefits have come from modeling.」
翻訳すると・・・
「多くのプロジェクトがモデリング作業を行っても、最終的にそれによる大きな利益が得られていない。DDDは、モデリングによって劇的な利益を得られたプロジェクトから、成功するパターンを抽出する。」
という意味になります。
つまり、DDDの最終的なゴールは 「モデリングによってソフトウェアの価値を高めること」 です。
モデルとは
DDDにおけるモデルは大きく分けて2種類になります。
- ドメインモデル:ドメインの問題を解決するためのモデル
- データモデル:データをどのように保存し、永続化するかを決めるためのモデル
この記事で「モデル」という言葉が出た際は、「ドメインモデル」のことを指してるんだなと思ってください。
例えば・・・
もうすぐクリスマスですが、皆さんはサンタさんに手紙を書いたことはありますでしょうか?純粋だった頃の私は下のように欲しいものリストを書いて、サンタさんに沢山のおもちゃを要求していました(がめつい・・・)。
翌朝、枕元においてあったプレゼントは一つだけだったので、「日本語で書いたから分かりづらかったんだ!英語で書けばきっと伝わる!」と思って英語で書いた時もありました(多分滅茶苦茶な英文)。
それはさておき、「手紙」のモデルを作成するとします。世の中には様々な手紙が存在しますが、一般的に考えられる要素を抽出してみると・・・
- 宛名
- 宛先(住所)
- 内容
- 差出人の名前
- 差出人の住所
といったものが挙げられると思います。しかし、現実の手紙には下記のような要素も含まれます。
- 作成日
- 郵送日
- 手紙のメーカー
- 手紙を書いた時の気持ち
他にも沢山の要素があると思いますが、これらの要素全てをソフトウェアに落とし込むのは不可能です。それに、問題を解決するために必要な要素はモデルに取り込みたいですし、不要な要素は取り込みたくないため、「どの要素を取り込むのか」という選択をすることになります。この取捨選択がモデリングにおいて重要であり、その成果物がモデルになるという訳です。
DDDの流れ
では、DDDという開発手法を用いてどのような流れで開発を行っていくのでしょうか?
- ドメインの理解
- 共通言語の確立
- モデリング
- コンテキストの分割
- ドメインモデルの実装
- テストと継続的な改善
一般的に上記のような流れで開発を行っていきますが、厳密な手順はなく、プロジェクトや状況によって順序が異なります。
1. ドメインの理解
まずは、ドメインに詳しい人(以後ドメインエキスパート)や関係者との会話を通じて、ドメインを理解する必要があります。開発者の部分的な理解のみで実装を進めてしまうと、要件とのズレが生じ、大きな修正コストがかかってしまう可能性があるからです。ドメインを理解することで、ビジネスや業界の特性、プロセスを正確に捉え、ドメインモデルをより精度高く構築することができるようになります。
2. 共通言語の確立
開発者とドメインエキスパートが共通の言語を使用することで、コミュニケーションをより円滑にすることができます。DDDでは、この共通言語のことを 「ユビキタス言語」 と呼びます。会話においては、慣れている日本語の方が扱いやすいとは思いますが、コードに落とし込む際に日本語から英語に変換する作業が生じるため、あらかじめ英語に統一しておくと有利に進められます。
3. モデリング
DDDにおいて、モデリング手法はこれといった決まりはありませんが、比較的シンプルで、少ないコストで大きな効果が期待できると言われているのが、 「sudoモデリング」 です。
- システム関連図(s):開発するシステムと関わりのあるアクターや外部システムとの関連を示す図
- ユースケース図(u):ユーザーの要求に対するシステムの振る舞いを定義する図
- ドメインモデル図(d):オブジェクト図を抽象化してドメインのルール/制約を表現する図
- オブジェクト図(o):ドメインモデルの具体値を記す図
sudoとは上記4つのモデル図の頭文字からとったもので、この4つのモデル図を活用してモデリングしていくことをsudoモデリングと呼びます。ここでは一例として、システム関連図を取り上げます。
システム関連図
サンタさんが一人で世の中の子供たち全員分の手紙を読んで、一人一人の欲しいものやお届け先を把握するのは大変です。そこで、一人一人の名前や住所、欲しいものをリストで管理して、外部のECサイトと連携して一気に発注してしまおうというシステムを作ることになりました。そのシステムの関連図が上の図になります。
「登場人物(人とは限らない)は誰なのか」、「どのタイミングで外部のシステムと連携するのか」といった情報が一目で分かります。
このように、4つのモデル図を作成しながらモデリングしていくことになりますが、ここで重要になってくるポイントは 「一度で完成させようとしないこと」 です。DDDにおけるモデリングは、ドメインの理解と要件の明確化に従って徐々に成熟させていくプロセスであり、完璧なモデルを一度で完成させるのではなく、ドメインエキスパートと協力しながら持続的な改善を重ねていくことが重要になってきます。
4. コンテキストの分割
2.共通言語の確立の時に説明した通り、ユビキタス言語(開発者とドメインエキスパートが使う共通の言語)を使用してモデルを作成していきますが、開発者とドメインエキスパートで共通のモデルを作成する上で、注意しなければいけない点があります。
例えば、飲食店で使うオーダーシステムを作るとします。
まずは、開発者とホールの人で話し合い、「料理」というモデルを作成します。ここでの「料理」は品名や価格、注文数、どこのテーブルの注文なのかといった情報を持ちます。
続いて、注文を受けた料理を作るキッチンの人とも話し合い、「料理」は品名の他に材料・分量、提供の順番といった情報を持つことになりました。
さらに、デリバリーも行っているため、配達人とも話し合いをした結果、「料理」に配送料や配送先といった情報も加わることになりました。
結果として、「料理」という一つのモデルが持つ情報が増えて、複雑になってしまいました。システムの規模が大きくなるほど、関係者全てで統一したモデルを作成することはより難しくなります。
このような問題を解決するためにDDDでは、特定の領域内(コンテキスト)でモデルを明確に定義し、それぞれの領域で共通の言語やモデルを統一することを目指しています。
上記の例で例えると、「注文」・「調理」・「配達」といったコンテキストにおいて、それぞれで統一された「料理」というモデルを作成することが望ましいということです。
5. ドメインモデルの実装
これまでの工程で作成したモデルをコードに落とし込んでいきます。実装の流れは(ざっくりですが)下記のようになります。※今回は概要なので、具体的なコードは省きます。
エンティティと値オブジェクト(バリューオブジェクト)の実装
エンティティは、ドメイン内で重要な意味を持つ対象や概念を表すオブジェクトのことであり、前節のオーダーシステムの例で考えると「料理」や「注文」などが該当します。例えば、料理のステータスを管理するためのルール(売り切れの状態の時は注文ができないようにする)などを定義することができます。
値オブジェクト(バリューオブジェクト)は、特定の値や属性の集まりをひとまとめにし、それらに関連するビジネスルールや機能を一つのオブジェクトとしてまとめるために使われます。例えば、料理エンティティに「価格」という属性があり、以下のようなルールが存在するとします。
- ゼロより大きい必要がある
- 税金や割引などの変更が適用される場合がある
これらのルールを値オブジェクトとして料理エンティティから切り離し、価格のデータとその操作をカプセル化することによって、再利用性を高めることができるようになります。
ドメインサービスの実装
ドメインサービスでは、複数のエンティティや外部リソースにまたがるビジネスロジックなどを処理します。基本的に、エンティティや値オブジェクトでは実装できない場合に使われます。例えば、従業員割引制度(従業員は半額で注文できる)がある場合、注文エンティティの他、従業員エンティティが関わってきたり、条件に基づいて割引を計算・適用する処理が必要になったりします。そのような場合にドメインサービスが使われますが、責務が集中しすぎないよう注意が必要です。
リポジトリの実装
DDDにおけるリポジトリの役割は、ドメインモデルと永続化ストレージ(データベースなど)の間で、データの取得、保存、変更などの操作を仲介することです。ここでのドメインモデルは、集約単位で扱うことが基本となります。
集約は、関連するオブジェクト(エンティティや値オブジェクト)のグループであり、一つのルートエンティティを持ちます。強い関連があるオブジェクト同士(例えば、注文エンティティと注文詳細エンティティなど)を集約単位で扱うことによって、データの変更や操作が行われた際に、データの整合性を確保することができます。
リポジトリはデータアクセスを担当する層なので、ドメイン知識を持たないように注意する必要があります。
6. テストと継続的な改善
ある程度実装が進んだら、テストを書いていきます。エンティティや値オブジェクトなどの単一のユニットテストや、結合テストを行い、期待通りに機能しているかを確かめる必要があります。もし期待通りに機能しなかったら、モデリングを見直したり、ドメインエキスパートにフィードバックをもらったりと、戻って改善を試みます。DDDでは、一度のモデリングや実装で完了させるのではなく、ドメインの理解を深めつつ継続的な改善を行うことで、ソフトウェアの価値を高めていくことに重きを置きます。
アーキテクチャやユースケース層など、色々と省いたところもありますが、以上がDDDを用いた開発手順となります。
まとめ
DDDとは、ソフトウェア開発における設計手法であり、複雑なビジネスドメイン(問題領域)に焦点を当て、そのドメインを理解し、解決するための手法となります。そして、DDDの最終的なゴールは、モデリングを通じて高品質なソフトウェアを開発することです。そのためには、ドメインエキスパートとの対話やモデリング、継続的な改善などが必要不可欠となります。
概要と言いつつ少し長くなってしまいましたが、ここまでお読みいただきありがとうございました!皆様に多くの幸せが訪れることを祈ってます🎅✨
参考文献
Discussion