ソフトウェアのアーキテクチャについて(その3)~ 良書とクリーンアーキテクチャ

以下の記事の続きです。 threecourse.hatenablog.com

ドメイン駆動設計についての書籍

ドメイン駆動設計周りを学ぶには、以下の2冊が良さそう。

有名な実践ドメイン駆動設計(www.amazon.co.jp/dp/B00UX9VJGW)を最初に読むと、主張が分かりづらかったり、概念の定義がはっきりとなされていないように思います。上記の書籍は、定義をできる限り明確に説明していたり、具体例で表現しているため比較的分かりやすいです。

クリーンアーキテクチャ

f:id:threecourse:20210501210013p:plain

クリーンアーキテクチャという良さげな設計があるらしく、上の図で表されます。
こちらもClean Architecture 達人に学ぶソフトウェアの構造と設計(www.amazon.co.jp/dp/B07FSBHS2V) を読んでも良くわからない部分があり、「ドメイン駆動設計入門」の著者のQiitaおよびGithubが参考になりました。

以下は私の理解です:

  • 具体的なクラスでなく、インターフェイスに依存することによって、柔軟性やテスト容易性を増す
  • 双方向に具体的なクラスに依存させると密結合になってしまう。だからといって双方向で抽象に依存させられるかというとそうではない。例えば、ユーザー作成処理をユーザーという具体的な概念なしに定義するのはむしろ不自然。
  • よって、具体的なクラスへの依存は片方向とし、逆はインターフェースへの依存としたい。Entities ← UseCases ← Controllers/Gateways/Presenters という方向への具体的なクラスへの依存は許容するが、逆方向は許容しない。
    つまり、UseCasesはEntitiesを利用できるし、ControllersはUseCasesを利用できるが、EntitiesはUseCasesをインターフェースを通してのみ参照でき、UseCasesはControllersをインターフェースを通してのみ参照できる

  • 処理は、インターフェイスで定義されるUseCaseとその実装であるInteractorで定義される

    • UseCaseのインターフェイスは、void Handle(UserCreateInputData inputData)のように入力を処理するだけである 
    • Interactorの実装は、IUserRepository, IUserCreatePresenterといったフィールドを持ち、入力を処理するときにレポジトリと連携してユーザ作成処理を行い、その結果を適切に表示させる、ということができる
    • UseCase/Interactorの入力、出力にはDTO (Data Transfer Object) として必要な値をまとめたオブジェクトを定義する
  • インターフェイスには、最終的に具体的なクラスを設定する必要がある(=依存オブジェクトを注入する必要がある)。各クラスのコンストラクタでいちいちセットする方法もあるが、この処理を行うためのDIコンテナと言われるライブラリがある。

ADOP

クリーンアーキテクチャを読んでいて、Entities, UseCasesとそれ以外を明確に分離することは有益であるが、小さなプロジェクトの初期段階から各インターフェイス・オブジェクトを作っていくのは非効率で煩雑ではないのかな。。
と思っていたら、上記の著者の方がADOPという実装パターンを考案されていて、納得感がありました。

https://nrslib.com/adop/

f:id:threecourse:20210501210236p:plain

私の理解では、以下のとおりです。

  • ドメインレイヤー(Domain Layer)、アプリケーションレイヤー(Application Layer )、アザーズレイヤー(Others Layer)に分ける。
  • Domain Layerには、ソフトウェアを適用しようとする対象領域(ドメイン)の事物を直接的に表現するコード、もしくは表現を支援するコードを記述する
  • Application Layerには、ドメインの事物を表現するコードを取り扱い、ソフトウェアで成し遂げたいユースケースを達成するコードを記述する
  • Others Layerには、アプリケーションレイヤーとドメインレイヤー以外のもの、例えばユーザーインターフェイスやデータベースに関するコードなどを記述する
  • Domain LayerのオブジェクトがApplication Layerに利用される、Domain Layer, Application LayerのオブジェクトがOthers Layerに利用されるのは問題ない。逆の場合は具体的なクラスを使用してはダメで、インターフェイスなどを通して汎化して利用する。