あくまで個人の「簡易的な勉強メモ」 であり、言い回しが原著と変わっている箇所があったり、網羅的に記述している訳ではございません。そのため語弊を与えてしまう危険性がございます。 こちらの記事よりも詳しく価値のある記事が他に多数ございます。原著とそちらを読まれることを強く推奨いたします。
クリーンアーキテクチャとは
かなり乱暴な言い方をすると
- 依存性は、より上位レベルの方針にのみ向けよ(一方方向)
- 制御の流れと依存方向は分離し制御せよ
参考文献) 世界一わかりやすいClean Architecture
知識が集約された上位レイヤーは保護されかつ、
詳細とは疎結合のためアダプターのように、フレームワークや DB などの付け替えが実現できる。
(もし、依存関係が逆転する場合は、DIPを用いて抽象に依存させる)
なぜ「クリーンな」アーキテクトが必要なのか
ソフトウェアアーキテクチャの目的は、求められるシステムを構築・保守するために必要な人材を最小限に抑えることである。
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ) P34
つまり
- 開発・保守を楽にする
- 必要なリソースを最小限に抑える
筆者が必要だと感じた事前知識(私見)
筆者は事前知識がほぼない状態で読書をしたため、知らない言葉を調べながら、6周以上も読み込みました。
その時に 事前に知っておけばもっとスムーズに読めたかな と感じた内容を記述いたします。
依存関係と制御の流れについての概念
- UML(クラス図)
- 矢印の向きの意味や、実線と点線の違いなど基本的な概念
- 依存先(JavaやJavaScriptだとimport)の把握
上位レベル/下位レベルの概念
- 下位レベル = 図22-1でいう外側 = 安定度が低くすぐに変更できる
- 上位レベル = 図22-1でいう内側 = 下位レベルから参照され内側にいくほど安定度が高くすぐに変更できない
アーキテクチャの基礎的な概念
オニオンアーキテクチャなどの優れたアーキテクチャの共通点を集約して汎用化したモデルがクリーンアーキテクチャ。
- レイヤードアーキテクチャへのざっくりとした理解
- ヘキサゴンアーキテクチャへのざっくりとした理解
- オニオンアーキテクチャへのざっくりとした理解
- DDD、主にDomain層、モデリングへの理解
- アーキテクチャではありませんが事前知識として知っていればより深くできると感じたため挙げました
筆者が参考にさせて頂いた記事です。
デザインパターンの基礎的な概念
- デザインパターンのメリットとざっくりとした概念の理解
筆者が参考にさせて頂いた記事です。
第Ⅰ部:イントロダクション
かなり前に記事にしたため、省略いたします。
第Ⅱ部:構成要素から始めよ:プログラミングパラダイム
同じく記事にしているため、省略いたします。
依存関係の逆転
- 図5-1でいうとML1が上向きになり、interface(抽象)を参照するようになる
- 依存関係の逆転はどこであっても逆転できる(図5-2)
OO(Object Oriented:オブジェクト指向)とは
- ポリフォーフィズムを使用することで、システムにある全てのソースコードの依存関係を絶対的に制御する能力
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
第Ⅲ部:設計の原則
筆者の以前の記事。
- SOLID原則は、中間レベルのモジュールやコンポーネントレベルの開発に使われるもの
第7章:単一責任(SRP)の原則
- モジュールはたった一つのアクター(ユースケース図における顧客/管理者など)に対して責務を負うべきである
- 変更する理由が複数あるべきではない
- 開発者が複数のクラスを追跡しないといけないジレンマがあるが、Facadeパターンなどで解決する
- メソッドが一つしかないclassばかり作られる可能性があるが、後々メソッドの数は大きくなるので問題ない
- また、各classにはprivateメソッドがいくつも含まれることになるだろう
第8章:オープンクローズド(OCP)の原則
- 既存のコードを変更せずに新しいコードの追加によって、システムの振る舞いを変更できるようにする設計
- つまり、新しい機能要件があっても既存コードの修正は最小限になるようにする
- 変更の理由が異なるものは「単一責任の原則」で分割する(図8-1)
- それらを「依存関係逆転の法則」で適切にまとめる
- OCPに最も適しているのはInteractorである。Interactorはビジネスルールを含んでいるため最上位レベルなので最も保護レベルが高い
- viewは最下位レベル
- 修正に対して「完全」に閉じることは不可能
第9章:リスコフの置換(LSP)の原則
- 個々のパーツが交換可能となるような契約に従わなければならない
第10章:インターフェイス分離(ISP)の原則
- 使っていないものへの依存を回避すべき
第11章:依存関係逆転(DIP)の原則
- 上位レベル(詳細側)が方針に依存すべき(図5-2)
- importなどの参照先がinterfaceや抽象クラスなどの抽象宣言だけを含むソースモジュールに限定する
- オブジェクトの生成にはオブジェクトの具像定義を含むソースコードの依存がされられないが、大半のオブジェクト指向言語では、Abstract Factoryパターンを使ってこの望まざる依存性を管理する
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
第Ⅳ部:コンポーネントの原則
- コンポーネントとはデプロイの単位である、Javaならjar、Rubyならgem
第13章:コンポーネントの凝縮性
- どのクラスをどのコンポーネントに含めれば良いのか?
再利用・リリース等価(REP)の原則
- 再利用の単位とリリースの単位は等価になる
閉鎖性共通(CCP)の原則
- 単一責任の原則をコンポーネントで言い換えただけ
- コンポーネントを変更する理由が複数あるべきではない
- 変更のタイミングがいつも一緒になるのであれば同じコンポーネントに属する
- オープン・クローズドの原則にも関係していて、閉鎖性はクローズドと同じ意味
全再利用(CRP)の原則
- どのクラスを同じコンポーネントに「まとめるべきではないか」
- 密結合していないクラスを同じコンポーネントにまとめるべきではない
- 使われる側のコンポーネントにある一つだけのクラス、というは再デプロイが必要になるので避ける
- つまり、不要なものには依存しないということ
- これはインターフェイス分離の原則を一般化したものである
第14章:コンポーネントの結合
非循環依存関係(ADP)の原則
- コンポーネントの依存グラフに循環依存があってはいけない
- 開発環境をリリース可能なコンポーネントに分割する
- 解決策は間にinterfaceを挟む(依存関係の逆転)
安定依存(SDP)の原則
- 安定度の高い方向に依存すること
- 安定度とは?
- 何かを変更する際に要する労力と関係している
- 安定させるには多数のコンポーネントから依存させるようにして、変更する理由を増やす(図14-5)
- 図14-5のXのようなコンポーネントを独立コンポーネントと呼ぶ
- 図14-6Yのようなコンポーネントは何も責務を負わず、3つの外的要因による変更の可能性がある。このようなコンポーネントのことを従属コンポーネントと呼ぶ
- 不安定が上、安定が下になるように構成を書き出すとわかりやすい
- 原則に違反してしまう場合は、依存関係逆転の法則を使う
- 全てのコンポーネントに高い安定度を求める必要はない
安定度の指標
- ファイン・イン
- 依存入力数。この指標は、コンポーネント内のクラスに依存している外部コンポーネントの数を表す
- ファイン・アウト
- 依存出力数。この指標は、コンポーネント内にある、外部コンポーネントに依存しているクラスの数を表す
- I(Instability)
- 不安定さ。I = ファイン・アウト÷(ファイン・イン + ファイン・アウト)。この指標は、ゼロ以上1以下の値になる。I = 0 が最も安定しているコンポーネントを表し、I = 1 が最も不安定なコンポーネントを表す。
- Javaの場合、importと修飾名を数えればIの値がわかる
安定度・抽象度等価(SAP)の原則
- コンポーネントの抽象度は、その安定度と同等でなければならない
- 安定度の高いコンポーネントは抽象度も高くあるべき
- 抽象度が高くなる方向に依存すべき
- 上位レベルのアーキテクチャや方針を示すものは頻繁に変更すべきではないので、安定度の高いコンポーネントに配置しなければならない
- 安定度が最大の(I = 0)コンポーネントを変更せずに済ませるには、オープン・クローズドの原則、つまり抽象クラス
抽象度の計測
- Nc: コンポーネント内のクラスの総数
- Na: コンポーネント内の抽象クラスとインターフェイスの総数
- A: 抽象度。A = Na ÷ Nc
- Aは0から1までの値をとる。この値が0であれば、そのコンポーネントに抽象クラスやインターフェイスが一切含まれないことを表す。この値が1であれば、そのコンポーネントには抽象クラスやインターフェイスしか含まれないことを表す。
- 除外すべき範囲(苦痛ゾーン/無駄ゾーン)(図14-13参照)
- 苦痛ゾーンにはデータベーススキーマや具象ユーティリティライブラリが該当する
- 主系列からの距離
- D(Distance): D = |A + I - 1| 0以上1以下の値となる。値が0の場合は主系列にある。値が1の場合は主系列から最もかけ離れた状態。
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
第Ⅴ部:アーキテクチャ
第15章:アーキテクチャとは?
- ソフトウェアアーキテクチャとはプログラマである
- プログラマを続けておかなければならない
- コードを書かず、より高いレベルの問題にフォーカスするというウソを信じてはいけない
- 優れたアーキテクチャは以下のことをサポートしなければならない
- システムのユースケース
- システムの運用
- システムの開発
- システムのデプロイ
第17章:バウンダリー:境界線を引く
- ソフトウェアの要素を分離し、お互いのことがわからないように制限する
- 早すぎる決定とは「フレームワーク/DB/Webサーバー/ユーティリティライブラリ/DI」であり、優れたアーキテクチャはこれらの決定に依存しない
- 早く決定すべきは「ビジネス要件/ユースケース」
- 境界線は「重要なもの」と「重要でないもの」の間にひく
- DBはGUIにとって重要ではないので境界線を引く
- ビジネスルールはDBInterfaceを使用してデータを取得・保存する。
- まとめ
- ソフトウェアアーキテクチャに境界線を引くためには、まずシステムをコンポーネントに分割する。
- その中からいくつかのコンポーネントがコアのビジネスルールになる。
- 必要な機能が含まれているその他のコンポーネントは、コアのビジネスには直接関係しないので、プラグインにしておく。
- 次にコンポーネントにコードを配置して、そこから一方向にコアのビジネスに向かって矢印を描く。
- これは、依存関係逆転の原則と安定度・抽象度等価の原則を適用したもので、依存性の矢印が詳細レベルから抽象レベルを指すようになっている。
第18章:境界の解剖学
- システムのアーキテクチャは、ソフトウェアコンポーネントとそれらを分離する境界によって定義され、こうした境界にはいくつもの形がある。
境界を超える
- 実行時に境界を超えるとすると、境界の向こうにある関数の呼び出しとデータの受け渡し
- 適切に境界を超えるためには、ソースコードを適切に管理する必要がある
- 図18-1のように制御の流れは「下位レベルから上位レベルに向かって境界を超える」
ローカルプロセス
- 強力な物理的なアーキテクチャの境界はローカルプロセスである
- ローカルプロセスは、通常のコマンドラインや同等のシステムコールで作成される
- そして、同じプロセッサまたはマルチコアのお同じプロセッサセットで実行される
- なお、アドレスは別々である
- ローカルプロセスは「上位コンポーネント」の一種と考える
- ローカルプロセスの境界を超える通信には、OSのシステムコール、データのマーシャルとアンマーシャル、プロセス間のスイッチがある
サービス
- 最も強い境界はサービスである
- サービスとは、一般的にコマンドラインや同等のシステムコールで開始されるプロセスのことで、物理的な場所に依存していない
- 下位レベルのサービスは上位レベルのサービスに「プラグイン」されるべきである
- 上位レベルのサービスのコードには、下位レベルサービスの物理的な情報(URIなど)を含めてはいけない
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
第19章:方針とレベル
- ソフトウェアシステムは方針を示したものである
- どのような場合でも、下位レベルのコンポーネントが上位レベルのコンポーネントに依存するように設計する
レベル
- 「レベル」の厳密な定義は「入力と出力からの距離」である
- 高い: 方針がシステムの入力と出力から「離れている」
- 低い: 方針がシステムの入力と出力から「近い」
- 最下位: 入力と出力を管理する方針
レイヤーについて(番外編)
- Enterprise Business Rules(Entities)
- Application Business Rules(Use Cases / Interactor)
- Interface Adapters(Controllers / Presenters / Gateways)
- Frameworks & Drivers(Web / UI / External Interfaces / Devices / DB)
参照文献)クリーンアーキテクチャ完全に理解した:クリーンアーキテクチャ概要
第20章:ビジネスルール
- ビジネスルールとはコンピュータで実装されているかどうかに関わらず、ビジネスマネーを生み出したり節約したりするルールのこと
- こうした自動化されていなくても存在するルールのことを「最重要ビジネスルール」と呼ぶ
- 最重要ビジネスルールには、いくつかのデータが必要になる。例えば、ローンであれば、貸付金残高、金利、支払いスケジュールなどが必要
- こうした自動化されていなくても存在するデータのことを「最重要ビジネスデータ」と呼ぶ
- 「最重要ビジネスルール」と「最重要ビジネスデータ」は密接に結びついており、こうしたオブジェクトのことを「エンティティ」と呼ぶ
エンティティ
- エンティティとは、PC内部にあるObjで、「最重要ビジネスデータ = プロパティ」を操作する「最重要ビジネスルール = 関数(interface)」をいくつか含んだものである
- エンティティは、DB/UI/Frameworkについて、何も気にする必要はない
- 「最重要ビジネスデータ」と「最重要ビジネスルール」を同じソフトウェアモジュールにまとめるだけで良い
- 企業全体の最重要ビジネスルールをカプセル化したもの
ユースケース
- 「interfaceのみ」で「実装はInteractor」
- ユースケースとは自動化されたシステムを使用する方法を記述したもの
- アプリケーション固有のビジネスルールを記述
- ユースケースには、エンティティの最重要ビジネスルールをいつ・どのように呼び出すかを規定したルールが含まれている
- ユースケースには、ユーザーからの見た目(ユーザーインターフェイス)については記述しない
- インターフェイスからやってくるデータとそこから出ていくデータを略式で規定しているだけ
- ユースケースを見ただけでは、そのappがwebなのか、シッククライアントなのか判断することは不可能
- ユースケースはObjでapp固有のビジネスルールを実装した関数を1つ以上持っている
- また、入力データ、出力データ、それらがやりとりする適切なエンティティへの参照といった、データ要素を持っている
- システムの全てのユースケースがカプセル化・実装されている
- ユースケースは、エンティティに入出力するデータの流れを調節し、ユースケースの目標を達成できるように、エンティティに最重要ビジネスルールを使用するように指示を出す
リクエストとレスポンスのモデル
- ユースケースは、入力データを期待し、出力データを生成する
- ユースケースがHTMLやSQLを知る必要はない
- ユースケースは、入力としてシンプルなリクエストデータ構造を受け取り、出力としてシンプルなレスポンスデータを戻す
インターフェイスアダプター(第22章)
- このレイヤーでは、エンティティやユースケースに便利な形式から、永続フレームワークに便利な形式にデータを変換する
- DBがSQLであれば全てのSQLはこのレイヤーに限定する必要がある
- また、このレイヤーには外部サービスなどの外部の形式から、ユースケースやエンティティが使用する内部の形式にデータを変換するアダプターも含まれる
フレームワークとドライバ(第22章)
- 円の一番外側で、DBやWebフレームワークなど
- 通常、このレイヤーにはあまりコードを書かない
- 書くとしても、円の次の内側とやりとりするグルーコードくらい
- FrameworkもDBも詳細のため、このレイヤーは詳細が詰まっている
- 被害が抑えられるように円の一番外側にある
境界線を超える(第22章)
- 図22-1 右下のようにコントローラー -> ユースケース -> プレゼンターとなる場合
- 依存関係逆転の原則を使い、interfaceやextendsを使う
- 図のようにユースケースの円の内側にあるinterfaceを呼び出して、円の外側にあるプレゼンターがinterfaceを実装する
境界線を超えるデータ(第22章)
- 境界線を超えるデータは、単純なデータ構造で構成されている
- 好みに応じて、構造体/データ転送Obj/単なる関数呼び出しの引数/ハッシュマップ/Obj
- 境界線を超えて渡すのは、独立した単純なデータ構造であることが重要
- 境界線を超えてデータを渡すときは、常に内側の円にとって便利な形式にする
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
第21章:叫ぶアーキテクチャ
- 一戸建ての設計図を見れば「一戸建て」の設計図とわかるし、図書館の設計図を見れば「図書館」だとわかる
- 同じく最上位のディレクトリ構成とパッケージは「会計システム」「在庫管理システム」と叫んでいるか?
- フレームワークにアーキテクチャを乗っ取られないようにする
テスト可能なアーキテクチャ
- Frameworkから少し距離を置いたものになっていれば、Frameworkを使うことなく全てのユースケースのユニットテストが実行可能なはず
- テストのために、サーバーやDBへ接続する必要はない
第22章:クリーンアーキテクチャ
- 様々なアーキテクチャがある
- ヘキサゴナルアーキテクチャ: DDDで採用され「ポートとアダプター」とも呼ばれる
- DCIアーキテクチャ:
- BCEアーキテクチャ:
- いずれも「関心事の分離」
- また、それぞれ少なくとも「ビジネスルールのレイヤー」と「ユーザーやシステムとのインターフェイスのレイヤー」と別れているため、以下の特性を持つ
- Framework非依存
- テスト可能
- UI非依存
- DB非依存
- 外部エージェント非依存
4つの円だけ?
- 図22-1の円は、概要を示したもの。従って、この4つ以外にも必要なものはあるだろう
- この4つ以外は認めないというルールはない
- ただし、依存性のルールは常に適用される
典型的なシナリオ
- 図22-2
出典元)Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
筆者は、これが Clean Architecture の具体的なクラス図であると考えています。
ただ、今まで登場してきた言葉の定義と若干違うところが混乱してしまいます。
詳細は、以下をご参考ください。
[実装クリーンアーキテクチャ/もうひとつの図]
https://qiita.com/nrslib/items/a5f902c4defc83bd46b8#%E3%82%82%E3%81%86%E4%B8%80%E3%81%A4%E3%81%AE%E5%9B%B3
[Laravelで実践クリーンアーキテクチャ/もうひとつの図]
https://qiita.com/nrslib/items/a5f902c4defc83bd46b8#%E3%82%82%E3%81%86%E4%B8%80%E3%81%A4%E3%81%AE%E5%9B%B3
第23章:プレゼンターとHumble Object
- プレゼンターはHumble Objectパターンの一種である
Humble Objectパターン
- Humble Objectパターンはユニットテストを実行する人が、テストしにくい振る舞いとテストしやすい振る舞いに分離するためのデザインパターン
- テストが難しい振る舞いは「Humble(控えめ)」
- Humbleから取り除かれたものはテストしやすい振る舞い
- GUIはテストが難しいが、Humble Objectパターンを使えば、2種類の振る舞いをPresenterとViewの2つのクラスに分けられる
プレゼンターとビュー
- ViewはHumble Objectであり、テストが難しい
- Presenterはテスト可能なオブジェクトである
データベースゲートウェイ
- ユースケースインタラクターとDBの間にあるのが「データベースゲートウェイ」
- appのがDBに対して実行するCRUDを含んだポリモーフィックインターファイスである
- ユースケースにはSQLの使用が許可されていないので、ゲートウェイでメソッドを用意
- ゲートウェイはDBのレイヤーにあるクラスで実装する
- ゲートウェイはHumble Objectでテストが難しい
- インタラクター(use case interfaceの実装)はHumbleではなくテストしやすい
第25章:レイヤーと境界
- システムは「UI」「ビジネスルール」「データベース」の3つのコンポーネントで構成されていると考えるとわかりやすい
- が、多くのシステムではこれよりもコンポーネントが多い
第26章:メインコンポーネント
- mainコンポーネントは、究極の詳細(最下位レベルの方針)である
- 最下位レベルであるため、このコンポーネントに依存(import)しているものはない
- 依存関係については、このmainコンポーネントにDIフレームワークを使って注入する必要がある
- mainはappのプラグインと考える
第28章:テスト境界
- テストはシステムの一部
システムコンポーネントとしてのテスト
- 全てのテストは同じである
- テストは非常に詳細で具体的であり、テストするコードに対しては常に依存している
- テストはアーキテクチャの最も外側にある
テスト容易性のための設計
- 少しのシステム変更で大量のテストが壊れる事を避ける
- 重要なのは「変化しないものに依存しない」こと
テストAPI
- 「変化しないもに依存」しないためには、テスト亜kら使用できるAPIを作成する
- このAPIは、UIガ使用するイントラクター及びインターフェイスアダプターのスーパーセットになる
- テストAPIの目的は、テストをappから分離すること
第Ⅵ部:詳細
第30章:データベースは詳細
- アーキテクチャの観点では、DBはエンティティではなく詳細
- 優れたアーキテクトは、システムのアーキテクチャが下位レベルの仕組みに汚染させる事を許さない
第31章:Webは詳細
- Webが登場してから「サーバーに処理」させるか「ブラウザに処理」させるかの振り子運動が始まった
- 従ってWebは詳細でビジネスロジックから切り離すべき
第32章:フレームワークは詳細
- フレームワークとの一方的な結婚によるリスク
- 一般的にFrameworkは依存性のルールに違反する傾向があり、アーキテクチャに難がある
- PJが大きくなるにつれてFrameworkに邪魔されることが多くなる
- Frameworkが進化する方向が自分にとって利益のないupgradeだったり、機能が突然廃止される
参考文献
- https://asciidwango.jp/post/176293765750/clean-architecture
- https://www.slideshare.net/AtsushiNakamura4/clean-architecture-release
- https://gist.github.com/mpppk/609d592f25cab9312654b39f1b357c60
- https://qiita.com/SDTakeuchi/items/14b0a227f1bf30c71f66
- https://qiita.com/little_hand_s/items/ebb4284afeea0e8cc752
- https://qiita.com/nrslib/items/a5f902c4defc83bd46b8
- https://www.techscore.com/tech/DesignPattern/
- https://techacademy.jp/magazine/9195
- https://www.fenet.jp/java/column/java_tips/6296/#Java3