Clean Architecture 達人に学ぶソフトウェアの構造と設計
メモとか感想
冒頭
- ソフトウェアはソフト(柔軟)なウェア
- 「アーキテクチャのルールはどれも一緒である」
- 1966年からハードウェアやIDEなどは進化してるけど、if文とかの基本構造はほぼ一緒
- 時代が進んで普遍的なルール(=CA)が分かってきたから紹介する
第I部 イントロダクション
第1章 設計とアーキテクチャ
- 設計が悪いと生産性が下がってくる
- 一時的に汚く書いたら早く実装できるという考え方は誤り
第2章 2つの価値のお話
- ソフトウェアの価値は「ふるまい」と「構造」の二つがある
- ふるまいを求められることが多いけど、ソフトウェア開発者が責任を持つべきなのは「構造」
第II部 構成要素から始めよ:プログラミングパラダイム
第3章 パラダイムの概要
- 以下の3つ
- 構造化
- オブジェクト指向
- 関数型
- いずれもプログラミングの機能に制約を与えるもの
第4章 構造化プログラミング
- Dijkstraさんがgoto文を数学的にディスって炎上した話
- プログラミングは数学的な証明ではなく科学的な証明が採用された
- 科学は証明可能ではなく反証可能だが、がんばっても反証できなければ目的に対して「十分に真である」と見なせる
- テストは「十分に真である」ことを示すプロセス
第5章 オブジェクト指向プログラミング
- 色々な定義があるけどどれも不十分で、筆者的にはポリモーフィズムで依存関係を制御できることが本質だと思う、とのこと
第6章 関数型プログラミング
- 関数型は変数を不変にする
- イベントソーシング
- 状態を持たず、履歴から状態を復元する考え方
- コンピューターリソースの増加によって現実的に実現可能になってきている
- 銀行の取引やgitの仕組みなど
III部 設計の原則
- SOLID原則は中間レベルのソフトウェア構造を作ることに役立つ
- 良いソフトウェアを作るためには、SOLID原則の上位にあるコンポーネントの原則やアーキテクチャも理解する必要がある(後述する)
第7章 SRP: 単一責任の原則
- 「モジュールは一つのことだけを行うべき」という意味ではなく、「変更する理由が一つだけであるべき」である
- モジュールはたったひとつのアクターに対して責務を負うべきである
- 同一ファイルに対してマージが発生するような状態(複数のアクターが変更を加えている状態)は違反してるかも
- クラスを分割した上でFacadeパターンでまとめておくと使いやすい
第8章 OCP: オープン・クローズドの原則
- 依存の方向を一方向にすること
- ビジネスルールが最上位のコンポーネントになり、他のどのコンポーネントの変更に影響されない
- インターフェースを切ることは、本体に影響を与えないためだけでなく、本体の情報を知りすぎない(情報隠蔽)ためでもある
第9章 LSP:リスコフの置換原則
- 利用する側が元のクラスと派生クラスを区別せずに使えるようにすべき
- 違反するとアーキテクチャに特別な仕組みが増えていって大変
第10章 ISP:インターフェイス分離の原則
- 依存しているコンポーネントは再コンパイル/再デプロイが必要になり、問題の元
第11章 DIP:依存関係逆転の原則
- stringクラスなど安定しているコンポーネントのことは気にしなくていい
- 変化しやすい具象クラスをできるだけ参照しない、継承も気を付ける
- 具象関数をオーバーライドせず、元の関数を抽象クラスにして複数の実装を用意したほうがいい
- 依存の方向と処理の方向は逆なので「依存関係逆転の原則」という
第IV部 コンポーネントの原則
第12章 コンポーネント
- マーフィーの法則(プログラムは、コンパイルとリンクに使える時間を使い切るまで肥大化する)にムーアの法則(計算機の速度やメモリや集積度は18か月ごとに倍になる)が勝った
- 現代ではコンポーネントプラグインアーキテクチャを気軽に使える
第13章 コンポーネントの凝集性
- 再利用・リリース等価の原則(REP)
- リリースバージョン毎に利用者が採用するかどうかを決められる形が望ましい
- コンポーネントのモジュールはまとめてリリースできる状態にするのが合理的
- 閉鎖性共通の原則(CCP)
- SRP(単一責任の原則)のコンポーネント版
- 同じタイミング/理由で変更するものはまとめ、異なるものは別々にする
- 同じタイミングで変更されることが多いクラスは一つにまとめておいた方が良い、というニュアンスが強い
- 全再利用の原則(CRP)
- 依存していないクラス(切り離せるクラス)が同じコンポーネント内に存在すべきでない
第14章 コンポーネントの結合
- コンポーネントの結合に伴って動作しなくなる場合がある
- 週次ビルド
- 4日自由に開発し、1日で結合する
- プロジェクトの規模が大きくなるほど結合にかかる時間は増え、効率が下がる
- 非循環依存関係の原則(ADP)
- コンポーネント単位でリリース可能な状況にしていおく
- その際に、有向非循環グラフである必要がある
- 要件の変化で循環が生まれそうな時は、依存関係逆転の原則(DIP)や、別の新しい依存するコンポーネントに移譲する
- 安定依存の原則(SDP)
- 安定度:簡単には動かせないこと
- 他からの依存が多いコンポーネントは(責務が多いため)安定度が高い
- 他への依存が多いコンポーネントは(外部要因で変更されやすいため)安定度が低い
- 安定度の指標
- ファン・イン:依存されている外部コンポーネントの数
- ファン・アウト:依存している外部コンポーネントの数
- I(Instability):不安定さ I = ファン・アウト / (ファン・イン+ファン・アウト)
- 0-1の値になる(0が最も安定している)
- 変更が発生しやすいコンポーネントは安定度が低い状態にしておく
- 安定度・抽象度透過の原則(SAP)
- 安定度の高いコンポーネントは抽象度も高くあるべき
- 安定度の高さが拡張の妨げになってはいけない
第V部 アーキテクチャ
第15章 アーキテクチャとは?
- ソフトウェアアーキテクトはプログラムを続けておかなければならない
- アーキテクチャの目的は「システムを適切に動作させること」ではなく「システムのライフサイクルをサポートすること」
- ソフトウェアをソフトに保つためには、できるだけ長い期間できるだけ多くの選択肢を残すこと
- システムが含む大きな2つの要素「方針」と「詳細」のうち、残すべき選択肢は「重要ではない詳細」
第16章 独立性
- コンウェイの法則
- システムを設計する組織は、組織のコミュニケーション構造をコピーした構造の設計を生み出す
- レイヤーの切り離し
- 単一責任の原則(SPR)と閉鎖性共通の原則(CCP)を利用して分離しておく
- ユースケースの切り離し
- ユースケースが他の異なるユースケースに影響しないように分離しておく
- 重複
- アーキテクトは重複を削除したがるが注意が必要
- 同じものとして扱うべき「本物の重複」と、別の役割を持つが似た構造を持つ「偽物の重複」あるいは「偶然の重複」を区別する必要がある
- データベースのレコード構造とある画面のデータ構造が似ている場合、そのままUIに渡したくなるが、これはほぼ確実に「偶然の重複」
- 切り離し方式
- 分類
- ソースコードレベル
- デプロイレベル(jarやdll、共通ライブラリなど)
- サービスレベル(ネットワークパケットで通信するサービスやマイクロサービスなど)
- 最初の段階で最適な方式を決めることはできない
- 初期はできるだけ切り離しておきつつ、ソースコードレベルで開発しておくと選択肢を残しやすい