初めまして、うらさくと申します
これから仕事や趣味で得た知見を備忘録がわりに書いていきたいと思います
それが少しでも皆様のお役に立てれば幸いです
さて、記念すべき初めての記事は読書感想文です
読んだ本はマーチン・ファウラー氏のclean architectureです
読むきっかけは会社の先輩から、アーキテクチャの勉強をするならまず読んでおいた方が良い本ということで紹介されたので読んでみることに
感想
- 書いている内容は素晴らしいが、エピソードが昔すぎてピンと来なかったり、話の脱線が多かったりで、やや冗長な感じはした
- 最初のSOLID原則や各原則に関してはわかりやすくまとめられていたのでおすすめです
- 翻訳は普通
概要
ソフトウェアアーキテクチャの目的は、求められるシステムを構築・保守するために必要な人材を最小限に抑えることである
プログラムを動かすために膨大な知識とスキルが必要となることはない。
(中略) 何かを「一度だけ」動かすのはそれほど難しくないのだ。
ソフトウェアを正しく動かすのは完全に別問題である。正しく動かすのは難しい。速く進む唯一の方法は、うまく進むことである
プログラミングパラダイム
関数型プログラミング
- プログラムの状態を変化させない関数を作成し、これを組み合わせて問題解決を行う手法
- 変数の再代入や入出力などによってプログラムの状態が変化することを副作用と言い、副作用を持たない関数を純粋関数と呼ぶ
- 基本的に純粋関数の組み合わせで作り上げる
- 状態をプログラムの中で扱い、変化させていくことは、プログラムの動作を分かりづらくさせたり、テストやデバッグを困難にする原因となる
- 競合、デッドロック、並行処理など
- 十分な記憶容量と処理能力があればアプリケーションを完全に不変、関数型にできる
- 例えば、口座の預金残高は変更されるので、変更がない取引履歴をデータとして残し、必要なときに計算させる
- 例えば、口座の預金残高は変更されるので、変更がない取引履歴をデータとして残し、必要なときに計算させる
オブジェクト指向プログラミング
- 継承
- カプセル化
- ポリモーフィズム
- この中で明確なOOの特徴は「ポリモーフィズム」だけである
- OOとは「ポリモーフィズムを使用することで、システムにあるすべてのソースコードの依存関係を絶対的に制御する能力」である。
構造化プログラミング
- プログラムを整理された構造の組み合わせによって構成すること
- 一般的には順接、反復、分岐の三つの制御構造によって処理の流れを記述すること
- かつてのプログラミングで多用されていた、指定した任意の位置に無条件に移動する “goto” 文を排除し、処理の流れがあちこちへ飛んで見通しが悪くなるのを防ぐことが重要である
重要ポイント
プログラミングパラダイムは、プログラマから能力を削除しているのである。新しい能力を提供しているものではない。
それぞれがネガティブな意図を持ち、何らかの規律を課しているのである。
これらのパラダイムは、何をすべきかを伝えるよりも、何をすべきでないかを伝えているのである。・構造化プログラミング ・・・ 直接的な制御の移行に規律を課すものである。
・オブジェクト指向プログラミング ・・・ 間接的な制御の移行に規律を課すものである。
・関数型プログラミング・・・代入に規律を課すものである。更にそれぞれは下記の関心事から生まれたものである
・構造化プログラミング ・・・ コンポーネントの分離
・オブジェクト指向プログラミング ・・・ データ管理
・関数型プログラミング・・・機能(注: 関数やサブルーチン)
設計の原則-SOLIDの原則-
コードを書く上で基本的な5つの原則(の頭文字)
単一責任の原則(Single Responsibility Principle)
- クラスを変更する理由は1つでなければならない
- 1つの変更を望む人たちに対する責任しか追わないようにする
- 想定外の重複やマージミスが発生してしまう
オープン・クローズドの原則(Open/closed principle)
- クラスは拡張には開かれていて、修正に対して閉じられていなければならない
- ソフトウェアはなるべく既存の成果物を変更せずに拡張できるべき
- 呼び出し側、実行側のどちらかが変更になた場合でも、両方変更をしなければならない
リスコフの置換原則(Liskov substitution principle)
- 派生型は基本型と置換可能でなければならない(継承したクラスは、継承元クラスと同じ動作をしなければならない)
- 正方形・長方形問題が有名な例
インターフェース分離の原則(Interface segregation principle)
- クライアントが利用しないメソッドへの依存を強制してはならない
- 使わないのに実装するのは意味がない、無駄である
- きちんとインターフェースをグループごとに分けて適切に使う
依存性逆転の原則(Dependency inversion principle)
- 上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュールも「抽象」に依存すべきである
- 変化しやすい具象クラスを参照・継承しない
- 具象関数をオーバーライドしない
- 変化しやすい具象を名指しで参照しない
コンポーネントの凝集性に関する原則
再利用・リリース等価の原則(REP:The Reuse/Release Equivalence Principle)
- コンポーネントを再利用するには、コンポーネントのリリース単位で行う
- 再利用の単位とリリースの単位は等価になる
- 例えば、Javaのクラスを修正した場合、クラス単位ではなくjar単位でリリース・再利用を行うのが理想
- 重要ポイント
- コンポーネントに含まれるクラスは、すべてが再利用されるか、すべてが再利用できないかのどちらかにすべき
閉鎖性共通の原則(CCP:The Common Closure Principle)
- コンポーネントを変更する理由が複数あるべきではない
- 同じ理由、同じタイミングで変更されるクラスをコンポーネントにまとめること。変更の理由やタイミングが異なるクラスは、別のコンポーネントに分ける
- 単一責任の原則(SRP)をコンポーネント向けに言い換えたもの
- 変更箇所が1つのコンポーネントに閉じていれば、変更後にデプロイする必要があるのはそのコンポーネントだけになり、そのコンポーネントに依存していないコンポーネントは再デプロイは不要となる。
- 重要ポイント
- 同じ理由、同じタイミングで変更されることが多いクラスは1つのコンポーネントにまとめておくべき
全再利用の原則(CRP:The Common Reuse Principle)
- 1つのクラスだけを再利用するということはめったになく、複数のクラスと組み合わせて使われることがほとんどのため、そうしたクラス群は1つのコンポーネントにまとめる
- どのクラスをひとまとめにすべきかということと、どのクラスをひとまとめにすべきではないかを表現している。
- インターフェース分離の原則(Interface segregation principle)を一般化したもの
Componentの結合に関する原則
非循環依存関係の原則(ADP:Acyclic Dependencies Principle)
- コンポーネントの依存グラフに循環依存があってはならない
- コンポーネント間で循環依存がある場合、コンポーネント単独でのデプロイ、リリースができずその部分が温床となりコンポーネント間での不整合が発生する
- アプリケーションが成長するにつれてコンポーネントの依存構造も細かく変わっていくため循環依存が生まれないよう適切に管理する必要がある
- コンポーネント間での依存は、具象クラスレベルではなく、インターフェース等の抽象レイヤーで依存すること
安定依存の原則(SDP:Stable Dependencies Principle)
- 安定度の高い方向に依存すること
- 安定度の高い
- 安定度の低い
- 依存されておらず、変更しやすいということ
- すべてのComponentの安全度を高くする必要はない
- 基本的に抽象度の高いレイヤーに依存すべきなため、「安定度の高い方向に依存」とは、言い換えると抽象度の高いレイヤーに依存せよとも言える
安定度・抽象度等価の原則(SAP:Stable Absstractions Principle)
- コンポーネントの抽象度は、その安定度と同程度でなければならない
- 安定度の高いコンポーネントは抽象度も高くあるべき
- 安定度の高さが拡張の妨げになってはいけない
- 安定度の低いコンポーネントはより具体的であるべき
- 安定度が低いことによってその内部の具体的なコードが変更しやすくなるため
- 苦痛ゾーン
- 安定度が高い具象コンポーネント
- 拡張も変更も難しい
- 安定度が高い具象コンポーネント
- 無駄ゾーン抽象的であるにも関わらず、それに依存するコンポーネントが存在しない
- 実装されないまま放置されているゴミ
アーキテクチャ(本編)
そもそもソフトウェアアーキテクチャとはなにか?
- ソフトウェアシステムのアーキテクチャは、それを構築した人がシステムに与えた「形状」である
- アーキテクチャの形状の目的は、そこに含まれるソフトウェアシステムの開発・デプロイ・運用・保守を容易にすることである。
優れたアーキテクチャを構築するには?
- それらを容易にするための戦略は、できるだけ長い間、できるだけ多くの選択肢を残すことである。
- 優れたアーキテクトは、方針と詳細を慎重に区別して、方針が詳細を把握することなく、決して依存することがないように、両者を切り離す。優れたアーキテクトは、詳細の決定をできるだけ延期・留保できるように、方針をデザインする。
詳しく
- ソフトウェアシステムは、「方針」と「詳細」に分割できる。
- 「方針」と「詳細」は慎重に区別して、「方針」が「詳細」に決して依存することがないように切り離す。
- 詳細の決定を延期や留保できれば、実験できる数が増え、挑戦できることが増え、決定しなければいけない時点までに入手できる情報が増える。
重複
ソフトウェア開発の世界では、「重複は悪」とされる。しかし、明らかに重複していたコードが、変更理由が異なるために個別に進化を遂げ、数年後には両者がまるで違ったものになっていることがある。これは「本物の重複」ではない。
重複を避けるために抽象クラスを作る事があると思う。注意しなければいけないことは、その二つは「本物の重複」かどうかを見極める事である。
仮に「嘘の重複」だった場合、徐々にソースコードは汚くなっていく。
境界線を引く
アーキテクチャとは、境界線を引く技芸である。ソフトウェアの要素を分離し、お互いのことがわからないように制限する。
境界線は「重要なもの」と「重要ではないもの」の間に引く。境界線を挟んだコンポーネントは、それぞれ変更の頻度や理由が違っている。
例えば最初からMySQLを使うことを決める必要はあるか?
もしかしたらファイルに出力する形式事足りるかもしれない。
なのでこの決定は「重要ではないもの」に分類される。
そういった「重要ではないもの」に振り回されないためにも境界線を引き、「重要なもの」から手を付ける必要がある。
叫ぶアーキテクチャ
あなたのアプリケーションのアーキテクチャはなんと叫んでいるだろうか?
最上位レベルのディレクトリ構造と最上位レベルのパッケージのソースファイルは、「ヘルスケアシステム」「会計システム」「在庫管理システム」と叫んでいるだろうか?
それとも「Rails」「Spring/Hibernate」「ASP」と叫んでいるだろうか?
フレームワークとの接し方
アーキテクチャはフレームワークから提供されるものではない。フレームワークは使用するツールであり、アーキテクチャが従うものではない。
あなたのアーキテクチャがフレームワークにもとづいているのなら、そのアーキテクチャはユースケースにもとづくことはできない。優れたアーキテクチャはユースケースを中心にしているため、フレームワーク、ツール、環境に依存することなく、ユースケースをサポートする構造を問題無く説明できる。
建築家の最大の関心事は、家がレンガで作られていることではなく、家が使用可能であることだ。冷静にフレームワークをみてほしい。疑いのまなざしでみてみよう。
確かに便利そうだ。だが、コストは?どのように使うべきか、どのように自分自身を守るべきか自問してほしい。ユースケースを重視したアーキテクチャをどのように維持するか考えてほしい。
フレームワークにアーキテクチャを乗っ取られないように、戦略をうまく策定しよう。
- これに対する筆者の意見
おそらく異論のある方もいらっしゃるだろう。私もすっきりしていない。
例えばWebアプリケーションを開発するときに、フレームワークを選定しないことがあるだろうか?だが、著者はこれまでの経験を引き合いに出しながら、圧倒的な説得力で力強く迫ってくる。
おまえのアーキテクチャは、見ただけでわかるようになっているのか?ドメインについて正しく叫んでいるのか?そのように問いかけてくる。フレームワークから着手しないことが本当に正しいことなのか、私にはまだよくわからない。本書で紹介されている例は、いずれも現代的なアプリの話ではない。正直、大昔の話を何度もされても困るのだが(翻訳も大変だし)
それでも「時代を超越した普遍のルール」が存在するとして、著者はいつまでも原理・原則に忠実であろうとする。本書で提唱されている「Clean Architecture」については、すでに著者本人がブログや講演などで情報発信していることもあり、見よう見まねでアーキテクチャの「同心円」を実装している例が数多く見られる。
だが、著者のように原理・原則に忠実であるためにも、まずは本書に目を通してもらいたい。そして、著者と対話しながら、自らのアーキテクチャをクリーンに仕手もらえれば幸いだ。
今至るところで議論されているクリーンアーキテクチャですが、その源となるなる理論がここにありますので読む価値はあると思います
初心者だと少し読みづらいので、増田さんの「現場で役立つシステム設計の原則」を読んでから挑戦されることをお勧めします