Clean Architectureについて
Google App Engine / Goで作っている個人プロジェクトにClean Architectureを適用してみた中で思ったことのメモです。
この記事ではClean ArchitectureとDIPとDIについて書いてます。
いわゆるDDDは意識してません。あくまでレイヤーについての感想です。
Clean Architecture とは
いくつかあるレイヤードアーキテクチャのうちのひとつです。
目的は関心の分離で、関心の分離をすると何が嬉しいかというと、のちの設計理解や維持管理が楽になることです。
図のように4層の円で構成されていて、それぞれ異なる役割(関心事)を表しています。
外側にいくほど実装の詳細を表現し、内側にいくほど抽象を表現します。
ちなみに層は4つのみという決まりはなく、必要であれば増やしても良いです。
依存関係は、外側から内側への一方向になります。
これは必ず従うべきルールで、内側から外側のコードを直接呼び出したらダメです。
依存関係は大事で、ここをしっかり押さえないとたぶん何を選択してもダメです。
以下、各層について私の解釈です。
External Interfaces
技術の詳細を担当します。
例えば、データの永続化にMySQLを使うのかSQLiteを使うのか、技術的な選択を内側の層で意識しなくて良いように薄くラッピングします。
ここにはロジックを書きません。
Interface Adapters
外側の層と内側の層をうまく取り持つ役割を担当します。
例えば、HTTPリクエストを受け取るControllerは、受け取ったHTTPリクエストを内層で扱える形に変換して渡す役目です。
基本それ以上のことはしません。
よく言われていることですが、仮にHTTPリクエストを内層(Use Cases)にそのまま渡すと、そのUse Casesは外界に依存することになり、依存関係は一方向というルールに反します。
使い回しという側面もありますが、そもそもの依存関係が破綻しているのでもうダメです。
Use Cases
ロジックの取りまとめとトランザクション管理を担当します。
繰り返しになりますが、WebやCommandなど外層の世界に依存しないように注意します。
1ファイルにつき1つの振る舞いを書くと見通しがよくて個人的に好みです。
Entities
技術的な関心から離れたロジックを担当します。
このロジックは、use case(用途)に限定しない一般的なものと解釈してます。
どこにも依存しない孤高の存在です。
依存関係逆転の原則(DIP)について
Clean Architectureを適用するにあたって、DIPの原則に従います。
(解説) 上位のモジュールは下位のモジュールに依存してはならない。 どちらのモジュールも「抽象」に依存すべきである。
雑にまとめると、interface(抽象)を用意して、処理の呼び出しをinterface経由にすると、実装の差し替えとか簡単になって便利!というものです。
内層から外層のコードを呼び出すときは、interfaceを経由します。
作用として、テストが書きやすくなり、DIと合わせることでcycle importsの解消にも役立ちます。
interfaceの置き場には議論の余地がありますが、私はEntities層に置くことが多いです。
依存性の注入(DI)について
コンストラクタやセッターメソッドでinterfaceを引数に取り、外部から実装を渡すパターンです。
実装の差し替えが簡単になり、融通の効くソースコードになります。
Use Cases層でよく使ってます
まとめ
大体Clean Architecture 翻訳と言ってることは同じ(でも自分の言葉にしておきたい)。
Clean Architectureは図が分かりやすくて、ソースコードに落とし込みやすい。
interfaceとDIを半ば強制されるので、自然とテストが書きやすいコードになる。
依存関係と合わせて単一責務の原則を守れば、酷いコードになりにくい。
コード量は増える。
続く。