「実践ドメイン駆動設計」を読んでドメイン駆動設計を学んだが、 いざ実践するにあたり、もっとパターンの具体例が知りたかった。 本書「ドメイン駆動設計入門」は、ドメイン駆動設計に登場するパターンに焦点を当てていて、 自分が補いたいと思っていた部分にバチっとハマった。
色々と新しい知識を得ることができたので、 読んでいて思ったことを読書メモとして残しておくことにする。
Chapter2 システム固有の値を表現する「値オブジェクト」
「そこにルールが存在しているか」「それ単体で取り扱いたいか」を重要視している著者の判断基準は、 自分が値オブジェクトを作るときに考えていることが明文化されたようで、スッと腑に落ちた。
Chapter3 ライフサイクルのあるオブジェクト「エンティティ」
ライフサイクルが存在し、そこに連続性が存在するならエンティティ。 ライフサイクルを持たない、またはシステムにとってライフサイクルを表現することが無意味な場合には、値オブジェクト。 例えばユーザーは、作成されて生を受け、削除されて死を迎えるので、まさにライフサイクルをもっているからエンティティになる。
Chapter4 不自然さを解決する「ドメインサービス」
値オブジェクトやエンティティに定義すると不自然に感じる操作をドメインサービスに定義していたので、 自分がやっていたことは間違いじゃなかった。
一方で、迷いが生じたらまずはエンティティや値オブジェクトに定義し、可能な限りドメインサービスは利用しない、 というのは中々強いルールだと思った。 ただ、何でもかんでもドメインサービスに持っていけてしまうので、 ドメイン欠乏症を避けるためには、こういったルールが必要だな。
Chapter6 ユースケースを実現する「アプリケーションサービス」
アプリケーションサービスが結果を返すとき、 幸いにして、データ転送用オブジェクトにデータを移し変えて返却していた。 ただ、徹底しているわけではない。 ドメインオブジェクトを返すかどうかに関しては無頓着だったな。
確かに、クライアントにドメインオブジェクトを返してしまったら、 クライアントがドメインオブジェクトを自由に操作できてしまう。 一人プロジェクトが多かったから問題にならなかったが、 もしチームで開発していたら、 プレゼンテーション層にドメインオブジェクトをふるまいを呼び出すコードが書かれてしまう可能性があったのか。 危なかった。
あとは、アプリケーションサービスに渡す引数はコマンドオブジェクトにすることを徹底しよう。
Chapter8 ソフトウェアシステムを組み立てる
ASP.NET Core MVC のコントローラーが受け取るオブジェクトは、 そのままアプリケーションサービスに渡していたな。 フロントから引き渡されるデータの入れ物と、 アプリケーションサービスの振る舞いを実行するためのコマンドは用途が違うので、 別のオブジェクトにした方が良い、 というのは言われて納得した。
Chapter9 複雑な生成処理を行う「ファクトリ」
ファクトリもオブジェクトなのか。 スタティックなクラスじゃないのね。 採番処理のためにデータベースにアクセスするなら、オブジェクトの方が良いのは確か。 過去にリポジトリに採番をさせたこともあるが、最近は ULID 使っているのでやらなくなった。
なので、採番処理が必要かどうかよりは、 コンストラクタ内で他のオブジェクトを生成するかどうかが、 ファクトリを作る指標になりそう。
Chapter10 データの整合性を保つ
ユースケースを実装するアプリケーションサービスでは、 整合性を保つ役目も担っているが、 C# なら TransactionScope を使えばいいというのは確かにと思った。 Entity Framework Core と Dapper、どちらを使う場合でも TransactionScope で対応できる。
今個人開発でやっているプロジェクトでは、ユニットオブワークにリポジトリを保持させているが、 ユニットオブワークの実装が結構複雑になってしまった。 TransactionScope が使えるなら、それがシンプルでいいか。
Chapter12 ドメインのルールを守る「集約」
集約にゲッターを用意せずに済むならそれが理想だけど、 永続化はどうすればいいんだろうと疑問だった。 通知オブジェクトというパターンがあるのか。
集約は通知オブジェクトのインターフェースを受け取り、 内部の情報を通知することで、 永続化が可能になる。 通知オブジェクトに関連するコードを用意するのは面倒だけど、 生成する開発者用補助ツールを用意し、 ルールではなくツールで縛る、 もといサポートする方針いいな。
Chapter13 複雑な条件を表現する「仕様」
エンティティや値オブジェクトに持たせるには複雑過ぎる条件を「仕様」オブジェクトとして抜き出すのかな。 仕様をリポジトリに渡して条件に合致する集約を取得する手法は、 FindBy 系のメソッドを乱立させずに済むので導入してみたい。 C# なら Expression Tree を上手く使えば色々表現できそうだ。
Chapter15 ドメイン駆動設計のとびらを開こう
ドメイン駆動設計に登場するパターンだけを取り入れる手法を「軽量DDD」と呼ぶなんて知らなかった。 自分の場合、仕事と個人開発、 どちらもDDDを採用できているプロジェクトは一人プロジェクトなので、 軽量DDDとあまり変わりないか…。
Appendix 付録 ソリューション構成
ソリューション構成では、MVVM や MVC の M、 つまりはモデルに当たるのがアプリケーション層とドメイン層なので、 プレゼンテーション層とは別プロジェクトにしていたが、 アプリケーション層とドメイン層は別プロジェクトにしたりしなかったり。
確かに、アプリケーション層とドメイン層を同じプロジェクトにしておけば、 ドメインオブジェクトのメソッドを呼び出せるクライアントをアプリケーションサービスに限定できるな。 次の開発から意識してみよう。