Enjoy Architecting

Twitter: @taisho6339

開発生産性を高めるアーキテクチャの本質 ~Clean Architectureを読んで~

どんな記事を書くか?

「Clean Architecture 達人に学ぶソフトウェアの構造と設計」という本を読み終えた。 非常に勉強になる本だったので、自分なりの解釈をしつつ学んだことをまとめる。 ※私個人の解釈を存分に含んでいる アーキテクチャは何のために設計するものか? ソフトウェアのアーキテクチャは何のためにあるものだろうか? 本書では「開発にかかる人員を最小にするものである」と述べてられている。 アーキテクチャが整備されていないプロダクトはどうなるかというと、 一つのクラス、レイヤが責務を抱え過ぎたり、修正、変更のスコープが必要以上に広がっていってしまうことになる。 FatなActivityやViewController、DB周りの変更が他の層に影響を及ぼすなどがその最たる例だ。 そして汚い設計のプロダクトは、時を経るごとにより汚いものにどんどん変貌していく。 アーキテクチャがイケていないと負債を増加させ、プロダクトが価値を生むまでのリードタイムをどんどん延ばしていってしまう。

開発スピードを遅くする条件とは?

では、システムのアーキテクチャ設計において開発スピードを遅くする条件とはどんなものがあるだろうか? それは主に2つあると考えられる。 - 早すぎる決定による設計の汚染 - 実装の詳細がビジネスロジックを汚染している

である。 順に説明する。

早すぎる決定による設計の汚染

決定を下すのはなるべくそのための知見が集まってから下すのが一番良い。 そうすることで決定の質を高め、将来的なムダな手戻りや運用開発コストを削減できることができる可能性が高まる。 例えば新規にシステムを開発するとき、使用するデータストアはどのタイミングで決めるだろうか? たいていの場合、設計フェーズの割と初期で決めることが多くないだろうか? ひどい場合だと要件定義の段階ですでに固定されているケースもある。 これは何を引き起こすだろうか? 開発の早いタイミングでMySQLを採用すると決め、その前提で開発をすすめていたものの、 システムの仕様への理解が進むにつれ、実は単純なファイルの読み書きで済んでしまうことがわかったとする。 だが開発はもうMySQL前提で実装されていて、大幅な手戻りになってしまうためこのまま進むことになってしまった。 そうすると下記のような開発、運用コストを余分に生み出してしまう。 - テーブル設計などRDBならではの余分な設計と実装コスト - リリース後のMySQLサーバの運用

このように「早すぎる決定」によって不必要なコストを長期的な未来に渡って抱え込むことになってしまう。

実装の詳細がビジネスロジックを汚染している

ビジネスロジックを記述しているクラスにWebやDBなどの「実装の詳細」を記載してしまうことで、 クラスの変更理由をビジネスロジックの変更以外にかかえてしまう。 つまりクラスの変更理由が複数になってしまう。 これはSOLID原則のS、単一責任の原則に違反している。 こうした違反が、将来的な修正、機能追加のコストを増大させてしまう。

良いアーキテクチャの本質

ではどのように設計してこれらの問題を解決していけばいいのか。 その本質は下記の図のような設計だ。 f:id:taisho6339:20200509175539p:plain

ドメイン、つまり業務知識とそれ以外をまずわけて考えるということだ。 システムにおいてまず最初に決まり、そして変わらないものはなにか? それはビジネスロジックだ。 ビジネスロジックを起点と考え、Web、データストア周りの実装をそこに付随するその他の関心事としてとらえる。 そして、ビジネスロジックは何にも依存せず、逆にその他がビジネスロジックに依存する仕組みにする。 こうすることで、ビジネスロジック以外は「プラグイン」に過ぎず、ビジネスロジックはそれ以外のものの影響を受けなくなる。 これが全てに通ずる良い設計の本質である。 ヘキサゴナルアーキテクチャ、オニオンアーキテクチャ、クリーンアーキテクチャなど種類が様々にあるが、本質はどれも一緒のはずだ。

クリーンアーキテクチャ

上述した本質をもとに、より現実的なレイヤーに分かれているアーキテクチャの一つがClean Architectureである。 f:id:taisho6339:20200509175556p:plain

Use Case層とEntitiesがビジネスロジック部分になる。 Entitiesに振る舞いを記述し、Use Case層でそれらを制御していく。Controllers、Gateways、PresentersはUse Caseからの入出力をWebやDB、UIなどが解釈できる構造に変換してあげる役割がある。 そしてこの同心円で一番大事なのは依存の方向である。 ビジネスロジックは何にも依存しない。その他の関心事がそれらに依存する形になる。 こうすることで出力先がWebになろうが、コンソールになろうがビジネスロジックは一切の影響を受けない。 データストアについてもしかりである。 一般的な3層アーキテクチャから来た人はデータストア側がビジネスロジックに依存する、というところに違和感を覚えるかもしれない。 よくある構成だと、Controllers(WebのIF) -> Service(ビジネスロジック) -> Repository(データアクセス)となっていて、ビジネスロジックがデータアクセス層を使って処理を構築するような仕組みになっているからだ。 f:id:taisho6339:20200509175613p:plain

だが、このような依存の方向にしてしまうとデータアクセスがビジネスロジックに影響を与えてしまう。 これではビジネスロジックは守られない。 ここで「依存性逆転の原則」を適用し、ビジネスロジック側でほしいデータアクセス処理のインターフェースを定義し、ビジネスロジックはインターフェースに対してアクセスする。 そしてデータアクセス層が、ビジネスロジックで定義したインターフェースを実装するという形にする。 これによって、データアクセス層がビジネスロジックに依存する形にでき、ビジネスロジックを独立させることができるのだ。 このように、ビジネスロジックとそれ以外の間にインターフェースで「境界」を作り、依存関係を制御してビジネスロジックを守るようなアーキテクチャになっている。 f:id:taisho6339:20200509175626p:plain

クリーンアーキテクチャで結局何が嬉しい?

こうしたアーキテクチャは何が嬉しいだろうか? それは下記になる。 決定を遅らせることができる 責務の切り分けが明確になる テストが容易になる

クリーンアーキテクチャを適用することで、ビジネスロジックから実装し、データアクセス層など他の層はスタブとして実装を進められる。つまりある程度実装を進め、ビジネスロジックへの知識が増えてから適切にデータストアや入出力(WebとかUI)の詳細を決定することができる。 これは「リーンソフトウェア開発」の決定を遅らせるという原則に則している。これにより、将来的なムダを削減し、ソフトウェアが価値を生むまでのリードタイムを短縮できる。 そして責務が明確になることで特定クラスの肥大化、変更スコープの肥大化を防ぐことができ、レイヤーが独立していることでテストのハードルが下がる。 ただ、これらのメリットの重要性はプロダクトがどのフェーズにいるかによって変わってくるだろう。 例えば新規開発時には、クリーンアーキテクチャは、責務の切り分けという観点では大げさに思えるかもしれない。 だが、決定を遅らせることができる、という点では多くの利点がある。逆にリリースしてエンハンスのフェーズに入ると、決定を遅らせることによるメリットを明確に感じる機会はそんなに多くないかもしれないが、責務が別れていてテストがしやすいことにより、負債を抱えて開発スピードを落とすことなく安定してグロースしていける可能性が高まる。

まとめ

アーキテクチャ設計もより視座を上げてみればプロダクトが価値を生むリードタイム削減に寄与する重要な要素である。 変わらないものであるビジネスロジックを依存関係から解放し、他をそこに付随するプラグインとしてあげることで「決定を遅らせ」、ムダな疲弊をへらす事ができる。 クリーンアーキテクチャではないアーキテクチャを採用したとしても、この本質は変わらないはずなので常に意識しておきたい。