クリーンアーキテクチャのメリットとデメリット
https://1.bp.blogspot.com/-rd13c_PYCHc/XexqupELsMI/AAAAAAABWiY/xPZ4z_kh9Wo5plD3VNp1PqRe66RbBX1IgCNcBGAsYHQ/s400/kotowaza_neko_koban.png
ある程度担当するサービスの実装を、チームに理解を得た上で、クリーンアーキテクチャへ移行を行い、またチーム内で浸透してきたので、個人的に感じているメリットとデメリットをまとめておきます。まだまだ道半ばなので途中経過ということで。メリットを述べるエントリは多数あるのでデメリットに関しては少し厚めにご紹介しています。
👍 メリット
アプリケーションのやっていることがわかりやすくなる
機能実装を行う際、クリーンアーキテクチャでは実装の責務を意識し、機能を層に分割して実装を行う。
Controller、Gateway、Usecase、Entity などの層があるが、アプリケーション内のビジネスロジック は常に Usecase にあるので、適切に運用されたコードに於いては Usecase の実装を見ればすべてが分かる様になっている。処理の中心は常にUsecase にある。
それ以外の細かな ビジネスロジック は Entity に存在するので付帯情報として確認すれば良い。
どのライブラリを使っているか、どのデータベースを使っているかなどは Gateway を確認すれば良い。
どの経路でUsecaseが利用されているかは Controller を見れば良い。
実装に係る責務が明確になっているため、処理を追いやすい設計になっている。
使用しているライブラリやデータベースを可換な状態で運用できる
アプリケーションを実装する上で重要となるのはどのライブラリを使うか、どのデータベースを使うかということではなく根本はアプリケーションが実現しようとしているビジネスロジック。
究極には、どのライブラリを使ってWebサーバーを起動しているか、どのデータベースを使って永続化しているかということは、ビジネスロジックに比較して比較的重要でないということになる。
※ 一方でパフォーマンスなどを考慮して実際にどのライブラリやデータベースを利用するかは慎重に選定されるべきだが別の話題になるのでここでは取り扱わない。
長期的なアプリケーション運用に於いては、利用しているライブラリの開発が停止したり、より高パフォーマンスな後発ライブラリがでてくることで、置き換えを迫られたり検討したりするシーンが発生するが、クリーンアーキテクチャで適切に実装されている場合は、依存するライブラリが抽象化されているため、本体のビジネスロジックをほぼ変更することなく、依存を置き換えることができる。
よく誤解されるものとして「データベースを置き換えることはほぼ無いのでクリーンアーキテクチャによる依存抽象化は過剰設計」という指摘を上げられるが、実装が可換になるレベルで抽象化されていることで単体テストをしやすい状態を維持できるであることや、責務による分割されていることでスパゲティコードになりづらいこと、処理の流れが一方方向になっていることで既存実装の理解を早めるなどの効果があり、実装が可換になっていることそのものは得られる恩恵のうちの一端でしか無い。
単体テストしやすく、テストの見通しを良く維持できる
アプリケーションの実装が責務ごとに層に分割されていることで、1つの実装が過剰に膨れ上がることが未然に防止される。
万が一過剰に膨れている場合責務が混ざっているなどの可能性を予見するので分割することを推奨する
基本的に「単一責務の原則」に従って1つの実装が行われるため単体テストを行う際に、テストがそれぞれ簡素で済む。
また、テストが簡素で済むことにより、複雑な前提条件を事前に組み立てることなくアプリケーションのテストを行える。
複雑な前提条件を隠蔽する初期化関数などが用意されることがあるがこれは本質的な見通しの改善を行っていない。
モックによる実装の差し替えが行えるランタイムにおいては、大量のモックを利用して単体テストを行う場合もあるが、そもそもその必要がなくなる。また、責務の境界をモックして良い境界として認識できるのでテストそのものもシンプルに記載することができる。
👎 デメリット
プロトタイピングを前提とする開発手法とは相性が悪い場合がある
「動くものをとにかく作りたい!」「すぐに動かしたい!」というような開発要件やシーンに於いては、すこし遠回りをした実装手段をとることになる。そのため、そのようなマインドセットで開発するチームでは文化的に定着しにくいことが考えられる。
長期的にアプリケーションを運用することを前提に立てばメリットではあるが、短期的には開発がまだるっこしく感じる場合もありデメリットとなりえる。
なれてくればある程度分割粒度を保ちながらプロトタイピングを行い、徐々にクリーンアーキテクチャの実装に寄せていくなどもできる。
素直にコードを実装するよりはコード量が増える
外部ライブラリを直接初期化して実装するなどの素直な開発手法に比較して、記述するコードは 1.5倍 程度に増加する傾向がある。
「Javaっぽい」「C#っぽい」という指摘を受けることがあるが、Ruby や Python などに代表される軽量言語に対してかなり厳密なクリーンアーキテクチャを取り入れるとその傾向が伺える。
Golang のような簡素さと読みやすさを重点に置くような処理系に於いては歓迎されないことが多い様子であるので利用は適切に判断が必要になる。
この場合、できなくはないが使うことが適切なのか?のような判断基準を持つことになる。
実装依存が疎になることでテストしやすい、将来的を見越したとき依存ライブラリの切り替えをしやすいなどの長期的メリットはあるものの、パット見ではかなり冗長なコードに見えるため短期的に担当する人員によっては開発意欲を削ぐ可能性を否定しない。
コード量が増える理由の例
責務ごとに層を分割して実装する、実装を可換にするために言語仕様にもよるが、実装に対応する interface をそれぞれ定義する必要がある。コレにより実装依存を抽象化し単体テストをしやすくしているなどの効果がある。
層をまたぐやり取りを行う場合、データアクセスオブジェクト(DAO) を定義し、レイヤーに対応する実装からは実行結果をDAOに詰めて返す必要がある。直接実装に依存しない効果がある。
適切に責務を分割できるようになるには理解と学習が必要
素直に開発を行う場合、取り入れたライブラリを即座に初期化して利用することで開発できるがクリーンアーキテクチャは実装がどのレイヤーに所属するのかを考慮した上で実装し、外部への依存は抽象化する必要がある。
ある程度開発経験のある場合や、設計に理解のある場合でなければ、適切な位置にを理解して実装を配置することが困難な場合が発生する。
チーム開発においては理解できる人員が1名でもいれば指摘や修正ができるが、それがない場合だたのスパゲティコードが出来上がる可能性もある。
したがって得られるメリットはあるものの、素直にコードを書く場合に比較して学習コストはかかる開発手法であることは理解しておく必要がある。
チーム開発の場合は適切なサポート体制を組んでおくことが望ましい。