Clean Architecture を読んだ
8月に和訳版が発売されて、Twitterのタイムラインを見てるとなかなか評判が良さそうだったので読んでみた。思えばアーキテクチャについての本は読んだことがなかったので、とても参考になった。
設計に関する原則は色々と紹介されていたが、正直、読了直後でもどれが何のことを表していたか...ってごっちゃになってしまっていたので、思い出したいときのために整理しておく。
Robert C.Martin,角 征典,高木 正弘 ドワンゴ 2018-08-01
SOLID原則
関数やデータ構造をどのようにクラスに組み込むのか、そしてクラスの相互接続をどのようにするのかについての原則
単一責任の原則(SRP: Single Responsibility Principle)
- モジュールを変更する理由がたったひとつ(のアクター)になるようにする
- 「各モジュールはたったひとつのことだけを行うべき」と誤解されがち
- 似たような処理でもアクターの異なるコードは分割するべき
オープン・クローズドの原則(OCP: Open-Closed Principle)
- 拡張に対して開いて、修正に対して閉じていなければならない
- 1988年、 Bertland Meyer が提唱
- ソフトウェアを変更しやすくするために、既存のコード変更より新しいコードの追加で振る舞いを変更できるようにする
リスコフの置換原則(LSP: Liskov Substitution Principle)
- 交換可能なパーツを使ってソフトウェア・システムを構築するなら、個々のパーツが交換可能となるような契約に従わなければならない
- 1988年、 Barbara Liskov が提唱した有名な派生型の定義。
- この原則違反の有名な例が「正方形・長方形」問題
インターフェイス分離の原則(ISP: Interface Segregation Principle)
- 使っていないものへの依存を回避すべき
依存関係逆転の原則(DIP: Dependency Inversion Principle)
- 上位レベルの方針の実装コードは、下位レベルの詳細の実装コードに依存すべきではなく、逆に詳細側が方針に依存すべき
- ソースコードの依存関係が(具象ではなく)抽象だけを参照しているのが最も柔軟なシステム
コンポーネントの原則
コンポーネント = デプロイの単位
凝集性に関する原則
再利用・リリース等価の原則(REP: Reuse-Release Equivalency Principle)
- 再利用の単位とリリースの単位は等価になる
- リリース番号がついてなければ、再利用するコンポーネントの互換性を確認できないので、当然の原則のようにも思える
閉鎖性共通の原則(CCP: Common Closure Principle)
- 同じ理由、同じタイミングで変更されるクラスをコンポーネントにまとめる。どちらかが異なる場合は別のコンポーネントに分けること
- SRPをコンポーネント向けに言い換えたもの
全再利用の原則(CRP: Common Reuse Principle)
- コンポーネントのユーザーに対して、実際には使わないものの依存を強要してはいけない
- ISPをコンポーネント向けに言い換えたもの
3つの原則は相関し、相反するものもある。開発時の利便性と再利用性・プロジェクトの進捗度によってコンポーネントの構成も変わっていく
結合に関する原則
コンポーネントの関連を扱う3つの原則
非循環依存関係の原則(ADP: Acyclic Dependencies Principle)
- コンポーネントの依存グラフに循環依存があってはいけない
- 循環依存があると、依存関係の混乱、ビルド(リリース)の順番を決めるのが難しくなる
- 循環依存を解消するために、依存関係逆転の原則(DIP)を適用する
安定依存の原則(SDP: Stable Dependencies Principle)
- 安定度の高い方向に依存すること
- 依存されているコンポーネントが多い程変更しにくいので、安定度が高いと考える
- 指標の例: 不安定度 = 依存しているコンポーネントの数 /
安定度・抽象度等価の原則(SAP: Stable Abstractions Principle)
- コンポーネントの抽象度は、その安定度と同程度でなければならない
- SDPと組み合わせると、抽象度の高い方向に依存すべきということ
- 違反してしまってるのがDBスキーマ。変更されやすく、抽象度は低く、他から依存されまくっている
- Stability-Abstraction Equivalency Principle とかじゃねーのかよって思った(どうでもいい)
その他のメモ
- ソフトウェアアーキテクチャの目的は、求められるシステムを構築・保守するために必要な人材を最小限に抑えること
- ソフトウェアの1つ目の価値(振る舞い)は緊急だが、常に重要とは限らない
- ソフトウェアの2つ目の価値(アーキテクチャ)は重要だが、常に緊急とは限らない
- プログラミングパラダイムとアーキテクチャ
- 構造化プログラミングは、直接的な制御の移行に規律を課す
- アーキテクチャレベルでは機能分割に役立つ
- オブジェクト指向プログラミングは、間接的な制御の移行に規律を課す
- ポリモーフィズムを使用することでソースコードの依存関係を絶対的に制御する能力
- 関数型プログラミングは、代入に規律を課す
- イベントソーシング(状態ではなく取引を保存する)
- 構造化プログラミングは、直接的な制御の移行に規律を課す
- ソフトウェアをソフトに保つには、なるべく多く選択肢を残すこと。その選択肢とは、重要でない「詳細」のこと
- 「方針」と「詳細」
- 方針とは、ビジネスルールや手順、システムの本当の価値
- 詳細とは、方針についてやりとりするものだが方針の振る舞いに影響を与えないもの。IOデバイス、DB、Web、Server、Framework、通信プロトコルなど
- レベルについて
- 「入力と出力からの距離」を「レベル」と定義する
- ソースコードの依存性をデータフローから切り離し、レベルと結びつけるべき
- アーキテクチャの境界を予測的に設計する(YAGNI否定)
感想
第Ⅳ部「詳細」では普段接している様々な技術(データベース・Web・フレームワーク等)を重要ではない「詳細」だと言い切っている。
(訳者あとがきにも似たようなことが書いてあるけど)自分もアプリをつくる時に言語は?FWは?どこで動かす?などをすぐに考えてしまうし、アーキテクチャもその都度都度で最適だと思うようにつくっていけばいいでしょと考えてしまっていたのは反省すべきポイントだった。
小さなクラス設計からデプロイ単位のコンポーネント設計まで、ちゃんと意識できてるって思えるまでは実践を積み重ねるしかないと思うのでこれから頑張っていこうと思う。