2019年7月31日水曜日

Clean Architecture 達人に学ぶソフトウェアの構造と設計

Clean Architectureについての解説で、いわゆる円形のあの概念などについても説明がある。

示唆に富む内容である。

まだ解説されていない文言が途中で出てきたりして、わかりにくい面もあるので、一度読み終わったらもう一度見返してみるのが良いと思う。


で、内容をざっくりまとめていく


リリースごとの人ごとのコード量は継続して計測したい。

優れたアーキテクチャとは、労働の最小化と生産性の最大化を実現する。

ソフトウェアの2つの価値、振る舞いと構造(アーキテクチャ)はどちらを優先すべきか?
どっちもなんだけど、往々にして振る舞いが優先される。

意図的に構造を優先する必要がある。
1.緊急かつ重要、2.緊急でないが重要、3.緊急だが重要でない、4.緊急でも重要でもない
3を最優先にする間違いを犯す
これをきちんと説明する必要がある
みんなが知っているわけではない。

アーキテクチャの関心事は、コンポーネントの分離、データ管理、機能、である。


プログラムの発展について

・構造化プログラミング
すべてのアルゴリズムが順接、反復、分岐の三つの組み合わせで記述できることに関連しており、gotoの批判などがある。

・オブジェクト指向とは、カプセル化、継承、ポリモーフィズムである。
ポリモーフィズムのパワーとは、デバイス非依存にできることだ。
そのI/Fが言語レベルで切れるところ。
それによって、ビジネスロジックからUIとIOを切り出して、独立なdeployまで可能にする。
プラグインアーキテクチャを可能にしている。

・関数型プログラミングは、値を可能な限り不変なものにして、可変なコンポーネントを意図的に作ってそこに集める。
また、ストレージと計算リソースが無限にあるならば全てを不変にできる。
初期値に対する変更の命令の全てを記録して計算を毎回全て行えばよいのだ。
codeのversion管理みたいに。


SOLIDの原則。

変更に強い、理解しやすい、コンポーネントの基盤として多くのソフトウェアで利用可のなソフトウェアを作るのを目的としている。


S
SRP Single Responsibility Principle 単一責任原則
モジュールを変更する理由が1つだけになるようにする。
組織が複数の役割を持たないようにするのと同じ。

O
OCP Open-Closed Principle
既存の変更よりも、新規追加による拡張を可能にしておくべき

L
LPS Liskov Substitution Principle リスコフの置換原則
ここのパーツを交換可能なように作らなければいけない

I
ISP Interface Segregation Principle インターフェイス分離の原則
使っていないものへの依存を回避すべきというもの

D
DIP Dependency Inversion Principle 依存関係逆転の法則
詳細側(UI,DB,NW)などが上位(アプリケーション)に依存すべきというもの。
上位は、詳細を知らない。ライブラリー開発をしていると割と一般的かも。

以下でそれぞれについて見ていく


・SRP Single Responsibility Principle 単一責任原則
CFOからの依頼で変更れた従業員情報がCOOの情報に影響を及ぼす感じ。
UML的なアクターが違えば、似ていてもコードは分離しておくべきということ。
集めたっくなったら、Facadeを利用してクラスを分割した後に集めれば良い。

・OCP Open-Closed Principle
拡張に対してOpenで変更にはClose
保護したいコンポーネント、影響を受けさせたくないコンポーネント
大抵の場合は、それはinteractor(usecase)になるが、それを他のモジュールが参照するようにすることで
UIやIOの変更が行われたとしてもビジネスには影響を与えたくない。

・LPS Liskov Substitution Principle リスコフの置換原則
派生クラスは、基本クラスの求めるものを全て満たさなければ行けない。
B extends A
なクラスがあったら、Aが満たすべきUnit testはBも満たすべきということ。

・ISP Interface Segregation Principle インターフェイス分離の原則
あるクラスに依存したとしても、使っていない公開APIが見えている状態が良くないので、Interfaceを必要な文だけ見られるようにして
それを見るようにしたほうが影響範囲が少なくなる。

・DIP Dependency Inversion Principle 依存関係逆転の法則
処理の流れとソースコード上の依存関係が逆になっているのをDIPとよぶ。
interactorからIOを呼び出すが、interactorで定義したi/fの実装がIOにあるような感じ
ビジネスのいちばん大事なところなどを安定化させて、変化しやすいところから切り出す効果がある。


コンポーネントについての説明。
SOLIDがレンガの組み方なら、コンポーネントは部屋の組み方。

コンポーネントとはDeployの単位。
単独でDeployできるシステム内の最小単位をさす

コンポーネントの凝集性とは、どのクラスをどのコンポーネントに入れるのか?の判断基準である。
原則は以下の3点
REP 再利用リリース等価の原則
CCP 閉鎖性共通の法則
CRP 全再利用の原則

・REP 再利用リリース等価の原則
まとめてリリース可能でなければならない。ライブラリがそれに当たる。
ライブラリならそれが当然であるが、コンポーネントもそうであるべき。
まとめようとすればするほど、同時にリリース可能だが、余計なクラスがコンポーネントに含まれることになる

・CCP 閉鎖性共通の法則
単一責務の原則をコンポーネント単位に言い直したもの。
同じタイミングで変更されやすいクラスは一つにまとめておけということ。
違うタイミングであるならば分けるべきということ。

・CRP 全再利用の原則
あるクラスに依存したときに、他の度のクラスにも依存しないということはまずない。
複数のクラスがセットで使われる事が多い。それらをまとめておくべき。
特定のコンポーネントに依存しているのであれば、そのコンポーネントに存在する全てのクラスに依存すべきである。
可能な限りそうすべき。

それぞれに反対の関係があるが、ちょっとすぐに理解するのは難しい。

REPだけを満たそうとすると、分けにくくなることによってCCP違反になり、依存しなくて良いものに依存してCRP違反になる
CCPだけを考えると、変更タイミングは違っても同時に使うもののリリースタイミングがずれてREP違反になるし、変更タイミングが近いというだけではCRP違反になる
CRPだけを考えると、コンポーネントがただ分割されるので、REP違反でCCP違反

非循環依存の原則(ADP)
循環参照は避けるべき、当たり前だが。
仮に作ってしまった場合は、DIPを利用して分割するか、循環のどこかに一つクラスを作って、そこで循環を断ち切る。

安定依存の原則(SDP)
安定しているモジュールに依存する方向で依存するようにする。
他から参照されているコンポーネントを参照するようにして、安定したコンポーネントは外部への参照を避けるようにすればよい。

安定度抽象度等価の原則(SAP)
安定したモジュールは抽象的であるべき。
いろいろなところから、参照されているなら、abstractにするかinterfaceにするかが良い。 

アーキテクチャとは
開発、運用、保守、デプロイ。
このサイクルをサポートするのがアーキテクチャ

開発
人数が増えてきたら、開発のためにアーキテクチャが必要でチームごとにコンポーネントを分けるべき

デプロイ
単一のアクションでデプロイしたい。デプロイの単位が分けれていれば可能なはず。

運用
サーバーを足すとかの運用がどうすればよいかが開発者がわかるようにすることが良いアーキテクチャ

保守
システムをコンポーネントに分割で切れいれば機能追加が可能なはず。
詳細の決定をできるだけI/Fの向こう側に押し込んで、決定を可能な限り遅らせる。

独立性
どうやって分割するのか?ということだ。
最初からマイクロサービスは明らかにやりすぎであるけど、将来的にそれができなくなったら困るわけで。
最初からマイクロサービスにも将来的にはできるようにソースコード単位で分割しておき
段々とデプロイー>サービスと分割の粒度を変えていくのが良い。
そのときに重要になってくるのが
ユースケースで分けるかレイヤーで分けるかである。

境界線
どこに境界線を置くのか?ということ。
具体的なDBとかUIとか外側の要素に境界を引いて、できるかだ気にははすことだ。
また、そのためにはコンポーネントをきちんと考えて分けておくことがそのために必要になる。

分割の戦略は
モノリス、デプロイ、スレッド、ローカルプロセス、サービス
などがある。

ビジネスルール
・Entityは、ビジネスデータとデータを操作するルールを記述した関数で構成される。
ビジネスそのもの。DBにもUIにも依存しない。特定企業のアプリにも依存しない。
・ユースケースは、アプリケーションそのもの。データのinput/outputなどを定義する。
ユースケースは、リクエストobjectとレスポンスobjectを必要とするかことがあるが、そのときにEntityを含めていはいけない。
Entityは最上位のclassで、リクエスト/レスポンスはより具体的なレイヤーだからだ。

アーキテクチャはやりたいことを記述しているか?
コードを見たら銀行のシステムとわかるか?
具体的なフレームワークが全面に出ていないことが大事。
ユースケースを見ればシステムが把握できるようにしておくのが良い

クリーンアーキテクチャーとは
・フレームワーク非依存
・テスト可能
・UI非依存
・データベース非依存
・外部エージェント非依存
レイヤーを作り依存を明確にすればよい。全ての実装のお手本になる気がする。

アーキテクチャーはテストしやすい部分としにくい部分で分離することが大事。

小さいプログラムでも分けることを想定する必要はある。現実に分けなかったとしても分けることを考えて常に監視し続けなければ行けない。

mainはクリーンアーキテクチャの最も外側に位置する。
Activityは外側なのである。

サービスで分けるということ。
サービスで分けたときに、ビジネスの中心が変わるときなどは各サービスの全てに影響がある。
例えば、通貨単位が増えたり法律の種類が増えるなどだ。
そうしたときには。全てに影響があるが、各Service内でEntityをそれぞれ作って対応すればdeployをずらすなの対応は可能。

テストAPIを作るのであればセキュリティissueにならないように決してdeployされないところに分けておくべきである。

データベース、Web、フレームワーク
これらは詳細だ。一番外にいるものだ。


Clean Architecture 達人に学ぶソフトウェアの構造と設計 Robert C.Martin
https://www.amazon.co.jp/dp/4048930656/ref=cm_sw_r_tw_dp_U_x_QOgqDbGETB94

0 件のコメント:

コメントを投稿