Clean Architecture を完全に理解したのでまとめてみた。
初めに
WED で Web フロントエンジニアとして働いている安田です。
最近、フロントのコードが汚くなってきたり、Go を書くことになって100%他の人が書いたソースコードを読んだりと、「今日の僕が読んでわからない」コードに触れる機会が増えてきました。
Clean Architecture の日本語版を読んだのですが、ボリュームが多く、まだ消化できない箇所があります。最低限、15章までで紹介されていた基本的な原理原則に関しては、覚えておきたい、身に付けておきたいため、振り返りとしてここに記載します。
プログラミングパラダイム (Ⅱ部)
構造化プログラミング (4章)
goto文が良くない。
goto文をつかないようにするために、機能分割をした設計をするのがベストプラクティスである。
オブジェクト指向型プログラミング (5章)
「カプセル化」「継承」「ポリモーフィズム」を用いて、システムのソースコードの依存関係を制御することを可能にしている。
下位レベルのモジュールを含んだ上位レベルのモジュールを作る。これの連続で設計できる。
関数型プログラミング (6章)
再代入を禁止することで、不変性を維持し、競合状態、デッドロック状態、並列更新の問題などが、発生しなくなる。
まとめ
プログラミングパラダイムはそれぞれ、
- 構造化プログラミング
直接的な制御の移行に規律を課すものである。 - オブジェクト指向プログラミング
間接的な制御の移行に規律を課すものである。 - 関数型プログラミング
代入に規律を課すものである。
「こうしたら良くなる。」というよりも「これをしてはいけない。」と言った具合に、
設計やコーディングから、何かを奪う規律のことである。
SOLID 原則 (Ⅲ部)
単一責任の原則 (SRP: Single Responsibility Principle) (7章)
○「個々のモジュールを変更する理由がたった1つだけになるようにすること」
×「個々のモジュールが一つのことだけを行うべき」
変更をしたいと思う人をアクターと呼ぶ。
モジュールはたったひとつのアクターに対して責務を負うべきである。
オープンクローズドの原則 (OCP: Open-Closed Principle) (8章)
既存コードの変更よりも新しいコードの追加によって、振る舞いを変更できるように設計すべき
変更の影響を受けずにシステムを拡張しやすくすること。
リスコフの置換原則 (LSP: Liskov Substitution Principle) (9章)
変更可能なパーツを使ってシステムを構築するなら、それぞれが交換可能な状態になっていること。
継承を巧みに使い、コードに特別な仕組みを少なくする。
特別な仕組みは、共通項がないので、理解するのに0から読むことになり、難しい。
なるべく共通項を見つけて、元の定義から派生する形にする。
インターフェイス分離の原則 (ISP: Interface Segregation Principle) (10章)
使ってはいけないものへの依存を回避すべき。
関係が薄いのであれば、分離して使うようにする。
クラス同士の結合度を下げる原則である。
依存関係逆転の原則 (DIP: Dependency Inversion Principle) (11章)
下位レベルのコードの変更に上位レベルのコードが影響を受けないようにするべき。
変化のしやすい具象クラスを継承・参照しないようにする。
しかしシステムを構築すると、どうしても具象クラスを継承し、具象クラスの変更に弱いモジュールが出来上がってしまう。これは避けられない。
なので、main コンポーネントに汚れ役を担ってもらう。
まとめ
SOLID の目的は
- 変更に強いこと
- 理解しやすいこと
- コンポーネントの基盤として、多くのソフトウェアシステムで利用できること
どんなに優れたレンガを使っても組み立て方が悪いと、でたらめな家が出来上がってしまうので、その組み立て方に関する原則となる。
コンポーネントの原則 (Ⅳ部)
ここで示す、コンポーネントとは、デプロイ単位のことである。
コンポーネントの凝集性の原則 (13章)
再利用・リリース等価の原則 (REP: Reuse/Release Equivalency Principle)
再利用の単位とリリースの単位は等価にすべき。
一つのコンポーネントを形成する別の複数(または一つ)のモジュールは、まとめてリリース可能な状態になっていないといけない。
閉鎖性共有の原則 (CCP - Common Closure Principle)
同じ理由、同じタイミングで変更されるクラスをコンポーネントにまとめること。変更の理由がタイミングが異なるクラスは、別のコンポーネントに分けること。
SRPをコンポーネントように言い換えたもの。
全再利用の原則(CRP - Common Reuse Principle)
コンポーネントはユーザーに対して、実際には使わないものへの依存を強要してはいけない。
これも同じく、一つのコンポーネントにまとめるべきクラスやモジュールを判断するための原則である。
ただ、CCP と比較すると、こちらは「どのクラスやモジュールをまとめるべきではないか」を伝えるための原則。
まとめ
コンポーネントの凝集性を考えたときに「ひとつのモジュールはひとつだけの機能を持っている」ということだけを考えていたが、ここで挙げられた三つの原則によって、もっと複雑であったことがわかった。
ただ、今時点では最適だと思っていた設計や考えも、プロジェクトが進み再利用性を重視することで、コンポーネントの再定義を行うことも必要である。
コンポーネントの結合の原則 (14章)
非循環依存関係の原則 (ADP: Acyclic Dependencies Principle)
コンポーネントの依存グラフに循環依存があってはいけない。
デプロイできる単位を最小にする。
規模が大きくなると、大勢の開発者が同じファイルに変更を加えることが起こる。そうなると、昨日まで動いていたプログラムが動かなくなっている。って状態になる。(ここではこれを「二日酔い症候群」と呼んでいる。)
二日酔いを覚ますために、一日かけて競合を取り除く、なんてことにならないために、循環依存を除去する必要がある。
安定依存の原則 (SDP: Stable Dependencies Principle)
安定度の高い方向に依存すること。
ここでの、安定度とは「簡単には動かせないこと」である。
つまり、変更しにくいコンポーネントは、変更しやすいコンポーネントに依存してはいけない。ということ。
安定度・抽象度等価の原則 (SAP: Stable Abstractions Principle)
コンポーネントの抽象度は、その安定度と同程度でなければいけない。
安定度の高いコンポーネントは、変更がされにくいように、
安定度の低いコンポーネントは、素早く変更ができるように。
まとめ
ここでは依存度に関することが書かれていたが、
システムには、いい依存と悪い依存がある。つまり、依存することが悪いというわけではなく、依存することで得られるメリットとのトレードオフを考える必要がある。
アーキテクチャ (Ⅴ部)
アーキテクチャの目的は、システムのライフサイクルをサポートするもの
優れたアーキテクチャがあれば、システムの開発・デプロイ・運用・保守を容易に行うことができる。つまり、コストを抑えられる
開発
開発チームが開発しやすいシステムにすべき。
開発規模が膨れると障害物が増え、開発がしにくくなるので、システムの規模に最適な人数で開発を行うのが良い。
デプロイ
システムはデプロイをしないと価値を生み出せない。
デプロイをすること自体のコストも抑えたいので、単一のアクションで簡単にデプロイができるような状況を作る必要がある。
運用
運用の問題の多くは、ソフトウェアアーキテクチャを変更しなくても、ハードウェアを追加すれば解決できる。
非効率な処理を行なっているサーバーも、サーバーを増設することで解消することができる。
人よりも課金をした方が安く済むので、運用の改善は一番コストが低いとも言える。
保守
運用とは反対に最もコストが高いもの。
無限に続く更新と修正の繰り返しは、いつか意図せぬ破壊を生んでしまう。
開発段階で、システムをコンポーネント化し、安定したインターフェイスを用意しておくことで、保守で起こりうる、意図せぬ破壊を防ぐことができる。
まとめ
ここで紹介されているアーキテクチャを活かすためには、常に選択肢を残しておく必要がある。
ユーザーストーリーがある程度揃っている、設計はまだない。この状態でフレームワークなどの詳細の部分を決めず、方針だけを決める。などの選択肢を残した状態で話を進めることは、後々の変更にも対応がしやすい。
最後に
どんなに素晴らしいアーキテクチャを用意していても、活用しなければゴミになってしまう。
また、活用できていてもチームの規模が膨れると、最初にメンテナンスしていた状態ほど綺麗な状態には復元できない。
なので、チームの規模もきちんと最適化する必要がある。
と言うのが、私がこの本を読んだ、ざっくりした認識です。
Clean Architecture の本を読み、いくつかの基本原則が出てきました。
感じたことはそこにはいつも Unix 哲学が背景にあるなということです。
僕自信全ての Unix 哲学を理解でき、実践できているわけではないですが、この仕事をするようになり一番最初にふれたものが、「Small is Beautiful」です。
これさえ守っておけばある程度、クリーンなシステムを開発ができると信じています。
SRP に REP などのコンポーネント化せよ、なんかはまさに Small is Beautiful のことだなと思って読んでいました。
改めて、日頃の僕のアウトプットを見つめ直すいい機会でした。
雑談
ここで、チームの最適化という話ですが、
Netflix が行なっている Full Cycle Developers というのが、最近聞いた中だと一番 Clean Architecture に倣った開発体制だなと思いました。
Full Cycle Developers at Netflix
ざっくり、
インフラチーム、フロントチームなどのチームではなく、システムのチームで動くことで、自分らで準備し運用するアーキテクチャへの理解度をチーム内で均一にすることができる。というものである。
日本語だと「フルサイクルエンジニア」というらしいです。
フルスタックエンジニアよりもやること増え、責任も重くなりますが、非常に面白いし、WED なら将来的にはできるんじゃないかなとは思ってます。
ただ、やってみようみたいな話が出た時に、できるレベルまでスキルをあげなくてはならないので、もっともっと頑張らないといけない。とここまで書いてて思いました。
Discussion