クリーンアーキテクチャを慌てて覚えた話。
まえがき
どこいっても、クリーンアーキテクチャの話は割とでてきます。私は自作フレームワークを幾度も考えて作っているので、思想とか設計は割と言葉の違い程度でわかっていると思っていました。しかし、ちょっと私の思想と違ったので、勉強してよかった。
問題点
文章で覚えるとキツい
そう、AIに聞いても文面だけだとピンとこない。ほんとはクイズ形式でだされるといい気がするけど、誰が作るんだって話。
呼び名、細分化が会社、PJT毎異なる
ここら辺はPJT毎名称を確認するか、コードみて判断することになる。
大きく分けて4層
覚え方:タイムライン
時系列で処理されるものなので、時系列で名称を覚えます。
インターフェースアダプタ -> ユースケース -> エンティティ -> インフラストラクチャ
です。
- インターフェースアダプタ層(コントローラー層、ハンドラー層、フレームワーク層と言ったり)
1-1. MVCのController、Presenter、Gateway
1-2. フレームワークのコア、中心的なプログラムです。
1-3. URLルーティング - ユースケース層
2-1. ユースケース(ビジネスロジック)でわかるならいいのですが、なんというか一番プログラマーがライトに書く場所。 - エンティティ層(モデル層、ドメイン層と言ったり)
3-1. バリデートや、状態管理。constとかの定義。 - インフラストラクチャー層(リポジトリ層と言ったり)
4-1. DBなど保存する処理
だいぶLAMPユーザ向けにかみ砕いて書いた。というか、なぜこうみんな説明しないのか。あと、無意識にMVCでもこういった構成で構築しているPJTはあるのでは?大規模なほどSQL系は別ファイルにしていたりしましたね昔。なので横文字が今時なだけで、昔からこういう構成のPJTって往々にしてあると思います。バリデートも別ファイルだったり。ただ小規模PJTだとこのクリーンアーキテクチャってのは大袈裟になると思います。ペットボトルをリサイクルにするか、火力発電の燃料にするか意見が割れるように、このアーキテクチャ(構造、構成)も意見が割れるのが正しいです。どちらにも正義があります。
AIに妄想自作フレームワーク品評してもらった
勉強ついでに自作するならこんな構成かなーと適当に記述したものをAIに品評してもらいました。
URI
案1) /v1/task ※ METHODで判定
案2) /v1/[get|post|put|delete]Task
main()
controller();//ルーティング処理
//層毎の共通関数。各actionで読み込まれる。もしくは目的に絞って読み込む。
/common/usecase.go
/common/domain.go
/common/repository.go
/action/v1/task/post.usecase.go
func postTaskUsecase
/action/v1/task/post.domain.go
func postTaskDomain
/action/v1/task/post.repository.go
/action/v1/task/get.usecase.go
..
ご提案の構成は、各レイヤの共通処理を共通ファイル(/common/usecase.go, /common/domain.go, /common/repository.go)にまとめ、機能ごとに/action/task以下に個別の処理ファイルを配置する方法になっており、以下の点でメリットがあります。
横断的関心事の集中管理:
共通のバリデーションやロギング、エラーハンドリングなど、複数のアクションで使われる機能はcommonディレクトリに集約できるため、修正や拡張がしやすくなります。
機能単位の局所性:
/action/task配下に全ての層(ユースケース、ドメイン、リポジトリ)のファイルを配置することで、その機能に関するコードを探しやすく、エンジニアが理解しやすい構成になります。
クリーンな層分離:
各ファイル名にレイヤの役割を明示することで、どの部分がユースケースで、どの部分がドメインロジックか、またどこで外部(DBなど)と連携しているのかが把握しやすいです。
ただし、いくつか注意点もあります。
レイヤの境界の明確化:
各アクション内のファイルが共通ファイルとどのように依存しているか、また依存関係が適切に逆転されているかを意識する必要があります。ドメインロジックが共通部分に隠れてしまうと、後々の変更で影響範囲が広がる可能性があります。
ディレクトリが肥大化する可能性:
プロジェクトが拡大すると、/actionディレクトリ下にも多くの機能が追加されるため、機能ごとにサブディレクトリで整理するなどの工夫が必要になるかもしれません。
URI設計について:
案1の「/v1/task」+HTTPメソッドと案2の「/v1/[get|post|put|delete]task」は、それぞれRESTfulな設計の観点でメリット・デメリットが異なります。RESTfulな設計では、リソース名は統一してHTTPメソッドで操作を分ける案1が好まれます。
全体として、今回の構成はVertical Sliceのアプローチと共通のレイヤ別共通処理の管理という観点で、十分に実践的で理解しやすいものだと思います。構成がプロジェクトやチームに合っているか、またレイヤごとの責務が明確に守られているかを意識しながら運用すれば、メリットを十分に活かせるでしょう。
あとがき
Vertical Sliceがどうも私は好きらしく、15年前からこういった近場にマトメがちではある。デメリットとして、役割がグダグダになるかもよ!って言われた。「ドメインロジックが共通部分に隠れてしまうと、後々の変更で影響範囲が広がる」指摘は確かにと思う反面、大規模だと横断的処理がきついよっていったのはAI君、キミだよ!!
あとがきその2
AIに聞いた。Ginでのクリーンアーキテクチャサンプルツリー構造。
project/
├── cmd/
│ └── main.go // アプリケーションのエントリーポイント。依存関係の組み立てやサーバ起動
├── internal/
│ ├── domain/ // ドメイン層: 業務の本質的ルールや概念の定義
│ │ ├── model/
│ │ │ └── task.go // 例:Taskエンティティ、関連のバリデーション、ビジネスルール
│ │ └── repository/
│ │ └── task_repository.go // ドメインで定義するリポジトリインターフェース
│ ├── usecase/ // ユースケース層: アプリケーション固有の業務プロセスを実装
│ │ └── task_usecase.go // 例:タスク作成、更新、削除のユースケース
│ ├── interface/ // インターフェースアダプタ層: 外部との接点を担う
│ │ ├── controller/
│ │ │ └── task_controller.go // Ginのハンドラー(コントローラ): ルーティングに対応する処理
│ │ └── presenter/
│ │ └── task_presenter.go // 出力データの整形など(必要に応じて)
│ └── infrastructure/ // インフラストラクチャ層: DB接続、外部APIとの連携、ルーティング設定など
│ ├── persistence/
│ │ └── mysql/
│ │ └── task_repository_impl.go // リポジトリインターフェースの実装(MySQLなど)
│ └── router/
│ └── gin_router.go // Ginのルーター設定。全ハンドラーの登録など
├── pkg/ // 共通ライブラリやユーティリティなど(必要に応じて)
└── go.mod // モジュール定義
で、慣れなんだと思うけど私が「Vertical Slice」派になりがちなのは、上記をみてもらうとmysqlの処理はリポジトリ(インフラ)層なのに、ドメイン層にもrepositoryディレクトリがあること。これはつまりデータ保存処理はリポジトリ層だけど、これの型とかはドメイン層の仕事だよってことです。
「リポジトリ層向けのドメイン層だよ♡」
初心者大パニックです。
そして、階層構造上めちゃめちゃ行ったり来たりしてしまう上に、似たような名前がありシンプルさに欠けている点です。TaskというAPI一つにそんなあっちこっち行かないで、Taskディレクトリ配下に層毎分けたファイルでそこだけでほぼ完結したらいいじゃんという感じです。
クリーンアーキテクチャのメリット
- そういう概念を知ってるだけで、プログラムをきれいに書ける
- 「言語変えます」「DB変えます」みたいなヤバいPJTにはおススメ
- GoみたいなClassのないinterface言語からClassのある言語に移る場合は、どのみちめちゃめちゃ作業
- 「RDBからNoSQLに変えます。」って時に、カラムや型かわらんやん?ならそれビジネスロジックだから共通の記述にしいひん?(ドメイン層)という発想。
- 「オーバーエンジニアリング」という俺ツエーができる
- テストの粒度を高められる点で、大人数で役割が細分化された大規模PJTならもってこい。
- AIドリヴンを意識すると、直近はOSSやクリーンアーキテクチャなどポピュラーな指向で導入したほうがAIとの親和性が高い。そのうちAIエージェントが伸びてそんなことさえどうでもよくなる。
- 金融や医療などお堅さがもとめられる分野で発揮する
- 経験浅めのエンジニアを排除できるまたは、クリーンアーキテクチャを事前に了解を得ることでコーディングルールなど厳格化に反対する人が現れなくなる。
Discussion