はじめに
「ドメイン駆動設計入門」(成瀬允宣 著)を読み終えました。
DDD(Domain-Driven Design)という言葉自体は業務で何度も聞いてきましたし、コードレビューで「これはドメイン層に書くべきでは?」のような指摘を受けたこともありました。
しかし、正直なところ「なんとなく」で理解していた部分が多かったです。
この記事は読書ログに近いです。「これ実務でやったやつだ」とか「ここ全然わかってなかった」と感じた場所を、テーマ別にまとめていきます。
ドメイン駆動設計をこれから学ぼうと思っている方、どの本を読もうか迷っている方はぜひ。
ドメインオブジェクトの使い分け:値オブジェクト・エンティティ・ドメインサービス
最初の難所は「値オブジェクトとエンティティの使い分け」でした。
判断の目安として実用的だったのが 「値にルールが存在するなら値オブジェクトにした方が良い」 という考え方です。
プリミティブな型のまま持つか値オブジェクトにするかで迷ったとき、これだとかなり判断しやすいです。値オブジェクトを採用するモチベーションは以下の4つ挙げられていました。
- 「表現力を増す」
- 「不正な値を存在させない」
- 「誤った代入を防ぐ」
- 「ロジックの散在を防ぐ(DRY原則)」
私はバリデーションをドメインオブジェクトの外に書いてしまうことが多かったので、この整理は刺さりました。
「全部エンティティでいいのでは?」と思い始めたところで、値オブジェクトの章に戻って読み直しました。
不変性があることで変数が少なくなり、バグの原因を減らせる。不変にできるものはなるべく不変にしておくだけ守れたらだいぶ変わる気がします。
使い分けの判断軸として「同一性を持つかどうか」に加えて「ライフサイクルを持つかどうか」も意識すると判断が少し楽になります。
ドメインサービスについては「不自然なふるまいのみを記述し、できるだけ避ける」という方針が印象的でした。全部ドメインサービスに書いてしまうとモデルが空っぽになり、その概念が何を表しているかわからなくなります。迷ったらモデルに書くことも意識したいです。
各層の責務を守る:リポジトリ・アプリケーションサービス・コントローラ
「どの層が何をすべきか」の話が続くのが5章〜8章です。
リポジトリの責務は「オブジェクトの永続化」だけです。ユーザーの重複確認のような処理もリポジトリには書かず、ドメインサービスで扱います。リポジトリだけインターフェースにするのは、ドメインの理解とリポジトリ固有の実装知識を切り離すためです。
アプリケーションサービスは「ユースケースを実現するもの」です。最初は「機能を実装するもの」と読んでしまいましたが、正確には違いました。機能を実現するための処理の流れを管理する層、という理解に直しました。
繰り返し強調されていたのが「ドメインのルールをアプリケーションサービスに書かない」こと。コードレビューで指摘されたときに「なんで?」と思っていた部分が、ここで繋がりました。DTOとCommandの使い分けについてはまだ「なんとなく」な部分があるので引き続き言語化していきたいです。
コントローラの責務は「入力の変換」だけです。コントローラはゲーム機のコントローラーと同じで、操作を伝えるだけでゲームの処理そのものは持ちません。このコラムの例えが一番イメージしやすかったです。
依存関係の設計(DIPとインターフェース)
依存関係逆転の原則(DIP)は、最初「矢印の向きが反転する」イメージで理解しようとして混乱しました。正確には、具体的な実装に依存していたものが抽象(インターフェース)に依存するようになった、という意味での「逆転」です。
業務でコンストラクタインジェクションをしたあとに引数を増やす作業をよくやっていました。あれが依存注入の恩恵で、注入していなかったらもっと大変だったんだなとやっと実感できました。ただ、コンストラクタインジェクションが増えすぎると凝集度が下がるという点も頭に入れておきたいです。
設計の規律:ファクトリ
ファクトリは「複雑なオブジェクトの生成処理をオブジェクトとして切り出したもの」です。
実務でファクトリを使っていたとき、当時は「ファクトリがあるだけでこんなに楽になるのか」と感動しました。この章でその仕組みの意味がはっきり言語化できた気がします。ただ「ファクトリを使わずに直接オブジェクトを作れてしまう」問題は残るので、コードベースが大きくなるほど設計規律の担保が難しくなります。
実装の流れとアーキテクチャ
新機能を実装するときの手順として「どんな機能が求められているか」「必要なユースケース」「概念と知識」を洗い出してから、ドメインオブジェクトを策定してアプリケーションサービスから実装する、という流れが示されていました(11章)。プライベートで実装するときにも手が止まることが多かったので参考になりました。実装に入る前に「どんな概念があるか」を整理する習慣をちゃんとつけていきたいです。
DDDの本質
DDDの本質は、ドメインとコードがモデルを介して繋がり続け、フィードバックを繰り返しながら改善していくことにあります。パターンをコードに当てはめるだけの「軽量DDD」は入り口として有効ですが、ドメインそのものの本質を理解することが重要だと学びました。
読み終えて
全15章、一冊を通じて感じたのは「知識として知っていることと、理由を理解していることは全然違う」ということでした。
コンストラクタインジェクションをしていたのも、リポジトリにビジネスロジックを書かないのも、どこかで「そういうものだから」で済ませていました。この本を読んで、それぞれに「なぜそうするのか」という理由が繋がってきました。
DDDは設計パターンを覚えるよりも、なぜその設計にするのかを理解することが先だと思います。まだアーキテクチャとテスト関連は理解しきれていないので、そこはもう一度読み返したいです。