Robert C. Martin
クリーンアーキテクチャ
- ビジネスロジックの本質(業務上の関心)
- 入出力処理等の副作用の隔離
クリーンアーキテクチャにおける方針 ビジネスロジックの明確化 フレームワークからの独立 外界との独立(ストアからの独立、UIからの独立)
イントロダクション
- ソフトウェアを「一度だけ」動かすのは、難しいことではない
ソフトウェアを「正しく」動かすのは、とても難しい
ソフトウェアを正しくすると以下のようなことが発生する
- 大量のエンジニアが不要になる
- 課題の管理が不要になる
- 欠陥(バグ)が減る
- 変更が容易になる
- 労力が最小になり、機能性と柔軟性は最大になる
アーキテクチャ
- アーキテクチャの魅力は構造である
- 重要性は変更コストで表現できる
- プログラムの構成要素をどのように組み立てるか
設計とアーキテクチャ
- 両者に違いはない
- ソフトウェアアーキテクチャの目的は、システム開発・保守するための人材を必要最小限にすること
数値で見る崩壊とエンジニアの給料
- リリースごとに生産性は低下、エンジニアの給与は増加
価値
- 完璧に動作するが変更できないソフトウェア(振る舞い)
完璧に動作しないが変更できるソフトウェア(アーキテクチャ)
ソフトウェア=ソフトであるべき
アイゼンハワーのマトリクス
- 振る舞いは、緊急だが重要ではない
- アーキテクチャは、緊急ではないが重要である
- よくある間違いは、緊急だが重要ではないタスクを最優先してしまうこと
ソフトウェアアーキテクト
- 機能よりもその構造を重要視する
機能を簡単に開発・変更・拡張できる構造(アーキテクチャ)を構築する
コンポーネントの分離
データ管理
機能
ソフトウェアは科学である
- テストはバグが存在しないことを実証できないが、バグが存在することは実証できる
- つまり反証可能なモジュール単位としての構造が大事
ポリモーフィズムの利点=依存関係逆転の原則
- 上位レベルの関数が下位レベルの関数を呼び出す場合、依存関係は
上位 -> 下位
だが、インタフェースを使用することで、下位 -> 上位
に依存する - ビジネスルールがUI、DBに依存するのではなく、UI、DBがビジネスルールに依存するが正しい
UI -> Business Rule <- DB
- 依存関係逆転の原則は、ビジネスルールへの依存を制御する
- 独立デプロイ可能性
- OOは、ポリモーフィズムを使用してソースコードの依存関係を制御する能力のこと
関数型プログラミング
不変性
- 競合、デットロック、並行更新は、可変を許容する変数が原因
- 副作用のコンポーネントは分離し、トランザクション制御により、上記の問題は全て失敗させればいい
イベントソーシング
- 状態ではなく取引(トランザクション)を保存する戦略
- ソースコードの管理システムと同じ
- 関数型である
SOLID
SRP (Single Responsibilty Principle) 単一責任の原則
- モジュールを変更する理由はひとつだけでなければならない
- モジュールはひとつのアクターに対して責務を負う アクターがポイント
- 給与システムの例
Scala class Employee { def calculatePay(pay: Pay): Unit def reportHours(report: Report): Unit def save(data: Data): Unit }
calculatePay()
は経理部が使用し、報告先は CFOreportHours()
は人事部が使用し、報告先は COOsave()
はデータベース管理者が使用し、報告先は CTOアクターが異なるため、上記の各メソッドは、別のクラスに分割するべき
データと関数に切り分け、分割する -> Facadeパターン
class EmployeeFacade {
def calculatePay = (new PayCalculator).calculatePay
def reportHours = (new HourReporter).reportHours
def save = (new EmployeeSaver).saveEmployee
}
class PayCalculator { def calculatePay = ??? // EmployeeData }
class HourReporter { def reportHours = ??? // EmployeeData }
class EmployeeSaver { def saveEmployee = ??? // EmployeeData }
class EmployeeData {}
// データを内部にもつFacade
class EmployeeFacade {
val employData: EmployeeData = new EmployeeData()
def calculatePay = (new PayCalculator).calculatePay
def reportHours = (new HourReporter).reportHours
def save = ???
}
class PayCalculator { def calculatePay = ??? // EmployeeData }
class HourReporter { def reportHours = ??? // EmployeeData }
class EmployeeData {}
OCP (Open-Closed Principle) オープン・クローズドの原則
- ソフトウェアの構成要素は、拡張に対しては開いていて、修正に対しては閉じていなければならない
- ソフトウェアの振る舞いは、既存の成果物を変更せず拡張できるようにすべきである
- コンポーネントの関係を単方向にする
LSP (Liskov Substitution Principle) リスコフの置換原則
- TODO
ISP (Interface Segregation Principle) インタフェース分離の原則
- TODO
DIP (Dependency Inversion Principle) 依存関係逆転の原則
- 下位レベルのモジュールは、上位レベルのモジュールに依存するべき