プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

Clean Architectureのディレクトリ構造

Clean Architectureを念頭に開発すると色々なクラスやインタフェースができる。

これらをどのようなディレクトリ(もしくはパッケージ)に配置すべきなのだろうか。 考えてみる。

なお以降は上図の用語を以下のように読み替えて欲しい。 読者には面倒を強いるが、個人的にはClean Codersの動画で学習している中でこれが染み付いてしまっている。

  • Input Boundary -> Usecaseインタフェース
  • Output Boundary -> Presenterインタフェース
  • Data Access Interface -> Gatewayインタフェース
  • Data Access -> Gateway実装

1. Clean Coders風

階層

<package name>
├── entities
├── gateways
└── usecases
    ├── usecase1
    └── usecase2

説明

  • entities = Entity
  • gateways = Gatewayインタフェース
  • view = View実装
  • usecases.usecase1 = Controller実装、Usecaseインタフェース、Usecase実装、Input Model、Ouput Model、Presenterインタフェース、Presenter実装、View Model、Viewインタフェース

コメント

「Clean Coders」の動画の中でUncle Bobが作ったサンプル。

発案者本人が作成した貴重な構成だが、謎が多い。

ユースケースごとに独立して開発できる点はメリットだが、usecasesディレクトリの中がごちゃっとしいる。ファイルが見つけにくいし、適切な境界があるか見えにくい。

サンプルではGateway実装が未作成だが、作成した場合、どこに入るのだろう?「infrastructure」みたいなディレクトリを新たに作るのだろうか?

どう解釈すべきか悩む。

参考

https://github.com/cleancoders/CleanCodeCaseStudy


2. Hexagonal Architecture風

階層

<package name>
├── adapter
│   ├── in ── web
│   └── out ── db
├── application
│   ├── domain
│   │   ├── model
│   │   └── service
│   └── port
│       ├── in
│       └── out
└── common

説明

  • app.application.domain
    • model = Entity
    • service = Usecase実装
  • app.application.port
    • in = Usecaseインタフェース、Input Model
    • out = Gatewayインタフェース、Output Model
  • app.adapter
    • in.web = Web Controller
    • out.db = Database Gateway
  • app.common = ユーティリティ

コメント

書籍「手を動かしてわかるクリーンアーキテクチャ」に掲載されている構造の一つ。

Springが前提のためpresenterが省略されている。

もしpresenterを含めるなら以下のような構成になるのだろうか。

  • app.application.port.outにPresenterインタフェースを配置
  • app.adapter.out.presentationなどにPresenter実装を配置

presenterがないのと命名がHexagonal Architecture風なことを除けば、原理原則に忠実な構成で、参考になる。

参考

https://github.com/thombergs/buckpal


3. ショートカット多めな構成

階層

app
├── application
│   ├── usecase1
│   └── usecase2
├── domain
│   ├── entity1
│   └── entity2
├── infrastructure
│   └── db
│       └── repository
└── presentation
    ├── usecase1
    └── usecase2

説明

  • app.domain.entity1 = Entity、Repositoryインタフェース
  • app.application.usecase1 = Usecase実装
  • app.infrastructure.db.repository = Repository実装
  • app.presentation.usecase1 = Controller実装、Input Model、Output Model

コメント

書籍「Go言語で構築するクリーンアーキテクチャ設計」で紹介されている構成。

DDD色が強く、Entityが厚い。Entityと同じ層にRepositoryインタフェースがある構成は好みが分かれそうだがDBへのシンプルなCRUD処理が大半ならこの方が管理しやすいかも。

他にも選択的にアーキテクチャを省略、変更している。

  • presenterが省略されている
  • Usecaseインタフェースと、Presenterインタフェースがない
  • Input Model、Output ModelがController実装と同じところにある
  • つまりAdapter層がほぼない

原理主義的ではないが、こういった短絡的設計が合っていることもあるので悪い訳ではない。色々と考えさせられる玄人向けサンプル。この辺りから混乱してきた。

参考

https://github.com/code-kakitai/code-kakitai/tree/main/app


4. フル構成

階層 + 説明

root
├── application
│   └── usecase = Usecase実装
├── domain
│   ├── adapter
│   │   ├── in = Usecaseインタフェース
│   │   │   └── input = Input Models
│   │   └── out = Gatewayインタフェース
│   └── model
│       ├── entity
│       └── value
├── infrastructure
│   ├── datasource
│   │   ├── adapter = Gateway実装
│   │   ├── entity  = ORM Entity
│   │   ├── mapper  = DTOへの変換
│   │   └── repository = データベース操作の実装
│   ├── externalapi = 外部API
│   │   ├── adapter
│   │   ├── config
│   │   ├── mapper
│   │   ├── request
│   │   └── response
│   └── transfer = 外部ストレージ
└── presentation
    ├── controller
    │   ├── request = request
    │   └── response = Output Model?
    └── handler = Controller + Presenter実装

コメント

あるブログで紹介されている構成。

やはりSpringが前提なのでpresenterが省略されてcontrollerと一緒にhandlerにまとめられているが、それ以外はかなり原理原則に忠実。

忠実であるが故にクラスの数がかなり多い。

参考

https://dev.to/borikatsu/kurinakitekutiyato-ddd-woyi-shi-sitapatukezigou-cheng-nituitenosi-an-29bp


所感

調査しているうちにどうでもいい気分になってしまった(笑)

「これが唯一の正解」というものはないと思う。

例えばWebアプリ前提の場合、Webフレームワークの制約上、presenterの省略はやむを得ない。

スレッドセーフが求められるケースではpresenterがviewModelをフィールドに保持する点がネックになり、presenterを排除したくなるかもしれない。

また、シンプルなユースケースの場合はInput Model, Output Model周辺は短絡的な設計にした方がコスパがよい。

構成も命名もケースバイケースで考えないといけないものだと思う。

調べた結果、逆に曖昧になってしまったが、とりあえず本家の構成をベースに、ゴチャっとしたusecasesディレクトリの中だけUseCases層とAdapter層に分けてみようかと思う。

app
├── entities
├── gateways
├── infrastructures
├── view
└── usecases
    └── usecase1
        ├── usecase
        └── boundaries
            ├── input
            └── output

説明

  • entities = Entity
  • gateways = Gatewayインタフェース
  • infrastructures = Gateway実装
  • view = View実装
  • usecases.usecase1.usecase = Usecase実装、Usecaseインタフェース、Input Model、Output Model, Presenterインタフェース
  • usecases.usecase1.boundaries
    • input = Controller実装、
    • output = Presenter実装、View Model、Viewインタフェース