【読書メモ】Clean Architecture

1章 設計とアーキテクチャ

・労力の最小化と生産性の最大化を実現するために優れたソフトウェアアーキテクチャを知る必要がある。開発組織は自信過剰にならず品質に向き合うべきだ。


2章 2つの価値のお話


・「振る舞い」と「構造」の2つの価値がある。
・「振る舞い」のフォーカスしすぎて、変更に弱い「構造」となっていまう場合がある。アーキテクトは変化に強い「構造」に対する責任を持つべき。


3章 パラダイムの概要


・構造化、関数型、オブジェクト指向といった変遷を経たが、それらはアーキテクチャに関連している。3つの関心事は「コンポーネントの分離」「データ管理」「機能」である。


4章 構造化プログラミング


・構造化プログラミングが価値を高めるのは反証可能なプログラミングの単位を作成する能力である。現代の言語ではGoto文サポートされていない。アーキテクトは簡単に反証(テスト)できるような設計を心がけるべき。


5章 オブジェクト指向プログラミング


・OOとはポリモーフィズムを用いてソースコードの依存関係を絶対的に制御する能力である。依存関係を制御の流れに合わせる必要がなくなるため、UI、ビジネスルール、DB等といったコンポーネントを独立したプラグイン化できるのだ。


6章 関数型プログラミング


・関数型プログラミングは代入に規律を課す。
・可変コンポーネントはデッドロック等の温床になりやすい。アーキテクトは可能な限り不変コンポーネントにコードを集約すべき。

SOLID原則:関数やデータ構造をどのようにクラスに、組み込むのか。またクラスの連関を教えてくれる。
1. 単一責任の原則 Single Responsibility Principle
2. オープンクローズドの原則 Open Closed Principle
3. リスコフの置換原則 Liskov Substitution Principle
4. インターフェース分離の原則 Interface Segregation Principle
5. 依存関係逆転の原則 Dependency Inversion Principle

7章 Single Responsibility Principal


・モジュールはたった一つのアクターに対して責任を負うべき。アクターの異なるコードは分割すべき。

8章 Open Closed Principal


・ソフトウェアの振る舞いを追加するには、既存のコードの変更は最小限にすべき。
・上位レベルのコンポーネントは下位レベルのコンポーネントが変更されたとしても修正不要な状態にすべき。

画像1

9章 Liskov Substitution Principle

・OOにおける設計原則であり、基本クラスを派生クラスに入れ替えても問題なく動くべき、という原則。
・SquareをRectangleの派生系として考えるのは難しい。なぜならばSquareは縦横同時に長さを指定するからだ。

10章  Interface Segregation Principle

・必要としないモジュールに依存することは有害である。・使っていない機能が存在するモジュールに依存していると、デプロイ時や障害発生時に課題が発生する。

11章 Dependency Inversion Principle

・ソースコードの依存関係が抽象のみを参照している状態が最も望ましい。
・安定した抽象とは 
    1. 変化しやすい具象クラスを参照しない
    2. 変化しやすい具象クラスを継承しない
    3. 具象関数をオーバーライドしない
    4. 変化しやすい具象を名指しで参照しない

画像2

12章 コンポーネントの簡単な歴史

・かつてはプログラムは再配置可能ではなかったため、プログラマはメモリ配置を考慮しなければならなかった。その後、再配置可能なバイナリを生成できるようになったこれがライブラリの始まり。外部参照できるようになったおかげで、個別にプログラムをコンパイルできるようになった。

13章 コンポーネントの凝集性

・どのクラスをコンポーネントに含めるかは開発時の利便性と再利用性のトレードオフを考慮すべき。

・再利用・リリース等価の原則(REP) 凝集性のあるクラスでコンポーネントが構成されているべき。またコンポーネントはリリース可能でなければならない。

・閉鎖性共通の原則(CCP) コンポーネントと変更する理由が複数あるべきではない。同じタイミングで変更されそうなコードは同じコンポーネントにまとまっていると良い。

・全再利用の原則(CRP) 一緒に使われることが多いクラスやモジュールは一つのコンポーネントにまとめましょう。


14章 コンポーネントの結合

・非循環依存関係の原則(ADP) 前日動いていたコードが翌日に動かなくなる”二日酔い症候群”を避けるには、リリース可能なコンポーネントへの分割が有効である。 この際、コンポーネントの管理は必要であり、かつ循環依存を避けなければならない。

・循環依存解消方法は2つある: 
1:依存関係逆転の原則を適応。A→Bという依存があったとしたら、Aにinterface用意し、Bがそのinterfaceを利用するようにすればいい。
2:AとBの両方が依存するCというコンポーネントを作成する。


・安定依存の原則(SDP) 変動が多いコンポーネントは変動が少ないコンポーネントから依存されるべきではない。 変動の大きいコンポーネントが依存されている場合は、DIPを適応して解消すべき。


・安定度・抽象度等価の原則(SAP) 安定度の高いコンポーネントは抽象度も高くあるべきであるという原則。抽象度が高くなるように依存すべき。


15章 アーキテクチャとは?

・アーキテクチャの目的は開発・デプロイ・運用・保守を容易にし、プログラマの生産性を最大化することである。


16章 独立性

・レイヤーにおける切り離し:UI、ビジネスルール、DBアクセスといったレベルでの独立。
・ユースケースによる切り離し:レイヤーが水平だとしたら、ユースケースは垂直により分割。
・切り離す方法は、ソースコード・デプロイ・実行単位(サービス)のレベルで考慮できる。どれを選択するかはプロジェクトの初期段階では判断が難しいだろう。


17章 バウンダリー:境界線を引く

・初期に境界線を引くことは決定をできるだけ遅くし、ビジネスロジックの汚染を防ぐ。

・早すぎる決定は生産性を下げる。優れたアーキテクトはフレームワーク・DB等の決定を最終時点まで引き伸ばせる。

・境界線は「重要」・「重要でない」ものの間に引くべき。例えば、GUIはビジネスルールにとって重要でないといった具合。 境界線はInterfaceの継承関係を通じて引くことができる。
Business Rules→DataBaseInterface ←||← DatabaseAccess → Database

18章 境界の解剖学

・モノリス以外のシステムでは複数の境界戦略(デプロイコンポーネント、スレッド、ローカルプロセス、サービス)を採用できる。


19章 方針とレベル

・コンポーネントは同じレベルでまとまっているべきであり、レベルとは「入出力の距離」である。また、コンポーネントは有向非循環グラフを形成していると良い。


20章 ビジネスルール

・最重要なビジネスルールはお金を発生させるルールであり、システム化されいる、いないにかかわらず存在している。
・エンティティとは最重要ビジネスルールを操作する関数とデータを含んだオブジェクトである。エンティティは、データベース、UI、サードパーティーのフレームワークから切り離して考えられるべき。
・ユースケースとはエンティティのルールをいつどのように呼び出すかを定義するオブジェクトであり、エンティティの下位に位置する。


21章 叫ぶアーキテクチャ

・優れたアーキテクチャはユースケースを中心にしており、フレームワークやツールには依存しない。システムの提供方法は不確定な状態でも、システムのユースケースは把握できているようにすべきである。


22章 クリーンアーキテクチャ

・クリーンアーキテクチャの同心円:外側から内側へ
【フレームワークとドライバ】デバイス・ウェブ・UI
    ↓
【インターフェースアダプタ】コントローラ・ゲートウェイ・プレゼンター・ビュー
    ↓
【アプリケーションのビジネスルール】ユースケース
    ↓
【企業のビジネスルール】エンティティ

・ソースコードの依存性は内側・上位にだけ向かっていなければならない。内側は外側を気にしない。

エンティティ:企業全体の最重要ビジネスルールをカプセル化したもの
ユースケース:アプリ固有のビジネスルールが含まれる。エンティティに入出力するデータを調整し、指示出しを行う。
インターフェイスアダプター:上位レイヤーに便利なフォーマットから外部エージェントに便利なフォーマットへの変換を行う。


23章 プレゼンターとHumble Object

・Humble Object Pattern: ユニットテストする人がテストしにくい振る舞いとしやすい振る舞いを分離するためのパターン。テストしにくいものはHumble Objectに切り出す。

・ViewはHumbleオブジェクトであり、Presenterはテスト可能なオブジェクトである。

・ユースケースとDBの間に存在するデータベースゲートウェイはHumble Objectであり、インタラクターはこのデータベースゲートウェイをスタブに置き換えることでテスト可能になる。

・ORマッパーはデータベースのレイヤーに所属する。ORMはゲートウェイインターフェースとデータベースの間にHumble Objectの境界を作るものである。


24章 部分的な境界部分的な境界を作る方法は3つある。

・独立してでデプロイ可能なコンポーネント複数をまとめる。
Strategy Pattern: ClientはService Boundaryというインターフェースをメソッドを呼び出す。ServiceImplという実装クラスが存在し、依存関係の逆転を行っている。
Facade Pattern: Facadeというクラスを用意し、全てのサービスはこのクラスのメソッドとなる。Serviceクラスが変更になるとClientクラスも再コンパイルが必要になる。
完全な境界を作るには非常に高いスキルと経験がいる。完全な境界を作るまでの代理として上記パターンを使うのも有効である。


25章 レイヤーと境界

・アーキテクチャの境界はあらゆるところに存在し、アーキテクトは常に境界を構築することを考慮しなければならないし、常に未来を予測することが大事なのだ。プロジェクト進行中も境界に関して常に見張る必要がある。


26章 メインコンポーネント

・Mainはクリーンアーキテクチャの最も外側・下位に存在するモジュールである。アプリケーションの設定(開発・テスト・本番)ごとに複数のMainコンポーネントを持つこともできる。


27章 サービス:あらゆる存在

・サービスはプロセスやプラットフォームを超えて関数を呼び出す存在であり、スケーラビリティや利便性を向上する役割を持つが、アーキテクチャにおいて必ずしも必要な存在というわけではない。・サービスは依存性ルールに沿って、コンポーネントと一緒に設計されるべき。そうすれば変化に対応しやすいアーキテクチャになる。


28章 テスト境界

・テストは依存性のルールに従うべきであり、アーキテクチャの最も外側に存在している。また独立してデプロイできる。
・脆弱なテストとは変化に弱いテストである。変化しやすいコードに依存したテストは良いテストとは言えない。
・アプリケーションからテストを切り離す目的としてテストAPIを導入すると良い。これにより全てのビジネスルールを検証できるようにすべき。


29章 クリーン組み込みアーキテクチャ


・ファームウェアは寿命が短いので、全てのコードをファームウェアにすることは望ましくない。クリーンアーキテクチャを組み込みソフトウェアの開発に活かすこともできる。

※30章以降は割愛

おまけ:Goでのクリーンアーキテクチャ

以下のサイトが非常に参考になったので、自分なりに分析してみた。
https://nakawatch.hatenablog.com/entry/2018/07/11/181453

画像3