ソフトウェアのアーキテクチャについて(その3)~ 良書とクリーンアーキテクチャ
以下の記事の続きです。 threecourse.hatenablog.com
ドメイン駆動設計についての書籍
ドメイン駆動設計周りを学ぶには、以下の2冊が良さそう。
「実践ドメイン駆動設計」から学ぶDDDの実装入門
www.amazon.co.jp/dp/B07S675HVM
amazonレビューを見るとスルーしてしまいそうになるが、用語や概念の定義をしっかり説明しようとしており、個人的にはこのテーマの本ではベスト。ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
www.amazon.co.jp/dp/B082WXZVPC
有名な実践ドメイン駆動設計(www.amazon.co.jp/dp/B00UX9VJGW)を最初に読むと、主張が分かりづらかったり、概念の定義がはっきりとなされていないように思います。上記の書籍は、定義をできる限り明確に説明していたり、具体例で表現しているため比較的分かりやすいです。
クリーンアーキテクチャ
クリーンアーキテクチャという良さげな設計があるらしく、上の図で表されます。
こちらもClean Architecture 達人に学ぶソフトウェアの構造と設計(www.amazon.co.jp/dp/B07FSBHS2V) を読んでも良くわからない部分があり、「ドメイン駆動設計入門」の著者のQiitaおよびGithubが参考になりました。
実装クリーンアーキテクチャ
https://qiita.com/nrslib/items/a5f902c4defc83bd46b8上記Qiita記事に対応するC#プロジェクト
https://github.com/nrslib/CleanArchitectureドメイン駆動設計入門に対応するC#プロジェクト
https://github.com/nrslib/itddd
以下は私の理解です:
- 具体的なクラスでなく、インターフェイスに依存することによって、柔軟性やテスト容易性を増す
- 双方向に具体的なクラスに依存させると密結合になってしまう。だからといって双方向で抽象に依存させられるかというとそうではない。例えば、ユーザー作成処理をユーザーという具体的な概念なしに定義するのはむしろ不自然。
よって、具体的なクラスへの依存は片方向とし、逆はインターフェースへの依存としたい。Entities ← UseCases ← Controllers/Gateways/Presenters という方向への具体的なクラスへの依存は許容するが、逆方向は許容しない。
つまり、UseCasesはEntitiesを利用できるし、ControllersはUseCasesを利用できるが、EntitiesはUseCasesをインターフェースを通してのみ参照でき、UseCasesはControllersをインターフェースを通してのみ参照できる処理は、インターフェイスで定義されるUseCaseとその実装であるInteractorで定義される
- インターフェイスには、最終的に具体的なクラスを設定する必要がある(=依存オブジェクトを注入する必要がある)。各クラスのコンストラクタでいちいちセットする方法もあるが、この処理を行うためのDIコンテナと言われるライブラリがある。
ADOP
クリーンアーキテクチャを読んでいて、Entities, UseCasesとそれ以外を明確に分離することは有益であるが、小さなプロジェクトの初期段階から各インターフェイス・オブジェクトを作っていくのは非効率で煩雑ではないのかな。。
と思っていたら、上記の著者の方がADOPという実装パターンを考案されていて、納得感がありました。
私の理解では、以下のとおりです。
- ドメインレイヤー(Domain Layer)、アプリケーションレイヤー(Application Layer )、アザーズレイヤー(Others Layer)に分ける。
- Domain Layerには、ソフトウェアを適用しようとする対象領域(ドメイン)の事物を直接的に表現するコード、もしくは表現を支援するコードを記述する
- Application Layerには、ドメインの事物を表現するコードを取り扱い、ソフトウェアで成し遂げたいユースケースを達成するコードを記述する
- Others Layerには、アプリケーションレイヤーとドメインレイヤー以外のもの、例えばユーザーインターフェイスやデータベースに関するコードなどを記述する
- Domain LayerのオブジェクトがApplication Layerに利用される、Domain Layer, Application LayerのオブジェクトがOthers Layerに利用されるのは問題ない。逆の場合は具体的なクラスを使用してはダメで、インターフェイスなどを通して汎化して利用する。