『Clean Architecture 達人に学ぶソフトウェアの構造と設計』を読む
初稿:
更新:
- 17 min read -
経緯
ソフトウェア開発から離れて10年以上が経過している。 昨年夏ごろからスマホアプリでも開発してみようと一念発起。
化石と化した脳みそをアップデートするために読んだ一冊。 刺さった箇所などを備忘録としてメモしておく。
本書の概要
書いているコードが変わらないのだから、どんな種類のシステムでもソフトウェアアーキテクチャのルールは同じ。ソフトウェアアーキテクチャのルールとは、プログラムの構成要素をどのように組み立てるかのルールである。構成要素は普遍的で変わらないのだから、それらを組み立てるルールもまた、普遍的で変わらないのである。(本書「序文」より) — アスキードワンゴより引用
備忘録・メモなど
アーキテクチャの目的
第1章の冒頭にそもそも「アーキテクチャって何ぞ?」がある。
ソフトウェアアーキテクチャの目的は、求められるシステムを構築・保守するために必要な人材を最小限に抑えることである。 — P34 第1章 設計とアーキテクチャ
なるほど。
何で設計するの?何でよい設計を求めるの?は、考えたことが無かったかもしれない。
設計?当たり前だろ、と誰に教わったか忘れたが、いつの間にかそういうもんと刷り込まれている。
確かに、後の修正でどんだけコストかかっても構わん、となればそもそもアーキテクチャなぞいらない。
もういきなりエディター開いてメインクラスにぜんぶ突っ込むワンファイル、ワンクラス、ワンメソッドの「トリプル・ワン・コーディング」でひとつよろしく、となるかもしれない。
そんなことしたら同僚に嫌われ、会社をクビになる。
つまり、会社も人もコストがかかることを嫌うよね、だから「構築・保守するために必要な人材を最小限に抑える」ために「エンジニアは黙ってクリーンアーキテクチャ」というのが本書の目的と理解した。
アーキテクチャの目的を達成するための目的
続いてあるのが以下。
アーキテクトは、機能を簡単に開発・変更・拡張できるアーキテクチャを構築するのである。 — P45 第1章 設計とアーキテクチャ
でどうやって「簡単に開発・変更・拡張できる」ようにするのか、の具体的な手法がようやく次章以降続く。
パラダイムがもたらしたもの
以下の一連のくだりは大事だなと思うとともにすごく共感することでもあった。
これらのパラダイムは、プログラマから能力を削除しているのである。新しい能力を提供しているものはない。それぞれがネガティブな意図を持ち、何らかの規律を課しているのである。これらのパラダイムは、何をすべきかを伝えるというよりも、何をすべきでないかを伝えている。 — P51 第3章 パラダイムの概要
いずれのパラダイムも我々にパワーや能力を与えてくれるものではない。過去半世紀にかけて我々が学んだのは、何をすべきではないかである。 — P76 第4章 関数型プログラミング
※ここで取り上げている対象は、構造化プログラミング、オブジェクト指向プログラミング、関数型プログラミングの3つ。
「何をすべきかを伝えるというよりも、何をすべきでないかを伝えている」
やっぱそうだよなあと首がもげるほど頷いた。
下記記事で「コーディングはできるだけ余計なことをしない、できれば仕組みでさせない様に環境を整えたほうが良いと思っています」と書いたが、アーキテクチャにおいても同じなのだよなと激しく納得した次第。
VisualStudioでC#を記述する際に「StyleCopAnalyzers(以降StyleCop)」というコード解析ツールを使用しています。チーム開発ではもちろん、個人開発においても整ったコーディングを支援してくれる強力なツールです。VSCodeでStyleCopを使う手順をまとめています。
設計の原則
解っている気ではいるが、詳細を理解し人に説明できるほどかと言われれば、力強く「ノー」と言わせていただきたい。
ということで、それぞれ別途調べるためにメモ。
- 第一責任の原則(SRP:Single Responsibility Principle)
- コンウェイの法則から導かれる当然の帰結。個々のモジュールを変更する理由がたったひとつだけになるように、ソフトウェアシステムの構造がそれを使う組織の社会的構造に大きな影響を受けるようにする。
- オープン・クローズドの原則(OCP:Open-Closed Principle)
- Bertrand Meyerが80年代に広めた原則。ソフトウェアを変更しやすくするために、既存のコードの変更よりも新しいコードの追加によって、システムの振る舞いを変更できるように設計すべきである。
- リスコフの置換原則(LSP:Liskov Substitution Principle)
- Barbara Liskovが提唱した有名な派生型の定義。1988年に誕生。要するに、交換可能なパーツを使ってソフトウェアシステムを構築するなら、個々のパーツが交換可能となるような契約に従わなければならないということ。
- インターフェイス分離の原則(ISP:Interface Segregation Principle)
- ソフトウェアを設計するには、使っていないものへの依存を回避すべきだという原則。
- 依存関係逆転の法則(DIP:Dependency Inversion Principle)
- 上位レベルの方針の実装コードは、下位レベルの詳細の実装コードに依存すべきではなく、逆に詳細側が方針に依存すべきであるという原則。 — P78 第3部 設計の原則
安定した抽象とは
DIに関するくだりで、安定した抽象を定義している箇所があり参考になったのでメモ。
- 変化しやすい具象クラスを参照しない。
- 変化しやすい具象クラスを継承しない。
- 具象関数をオーバーライドしない。
- 変化しやすい具象を名指しで参照しない。 — P104 第11章 DIP:依存関係逆転の原則
コンポーネント単位での安定した依存関係
前の章ではプログラム、この章ではコンポーネントにおける依存性についての説明がなされている。 以降、依存性とそれに関連し「境界」というキーワードが頻発する。
冒頭の「機能を簡単に開発・変更・拡張できるアーキテクチャ」を実現するための重要なポイントなのだろう。
コンポーネントにおける依存関係の安定度の指標が役に立ちそうなのでメモ。
- ファン・イン:依存入力数。この指標は、コンポーネント内のクラスに依存している外部コンポーネントの数を表す。
- ファン・アウト:依存出力数。この指標は、コンポーネント内にある、外部のコンポーネントに依存しているクラスの数を表す。
- I(Instability):不安定さ。I = ファン・アウト ÷ (ファン・イン + ファン・アウト)。この指標は、ゼロ以上1以下の値になる。I = 0 が最も安定しているコンポーネントを表し、I = 1 が最も不安定なコンポーネントを表す。 — P125 第14章 コンポーネントの結合
アーキテクチャとは?
冒頭で「アーキテクチャの目的」があり、以降その実現手法について説明がされてきたが、ここ15章でようやく、そもそもの話しに入る。
ソフトウェアシステムのアーキテクチャは、それを構築した人がシステムに与えた「形状」である。その形状を生み出すためには、システムをコンポーネントに分割し、コンポーネントをうまく配置して、コンポーネントが相互に通信できるようにする必要がある。 — P148 第15章 アーキテクチャとは?
「形状」、とある。
その形状を形作るにあたりコンポーネントに分割し、それぞれ通信、つまり依存関係が生じる。境界の定義が必要になる。
前章まで、依存性、境界についてしつこく語られていたのは、つまりアーキテクチャとは上記のものだから、ということか。
そして続けてこうある。
アーキテクトの目的は、方針とは無関係に詳細を決めながら、方針をシステムの最も重要な要素と認識するシステムの形状を作ることである。こうすることで、詳細の決定を延期や留保することができる。 — P151 第15章 アーキテクチャとは?
コンポーネントに分割し互いに通信し合う形状、つまりアーキテクチャは、さらに「方針」がもっとも重要な要素となり、詳細は留保したり延期が可能な形状でなければならんとな。
分割したコンポーネントの関係性のこと?
分割されたコンポーネントは、「方針」を担うものとその他「詳細」に分けられ、「詳細」は留保や延期が可能、極端に言えば「重要ではない」ということか。
それは、終盤で語られる「詳細だから重要ではない」を連発するくだりで解った気がする。
データベースは詳細だ。ウェブアプリかどうかは詳細だ。フレームワークは詳細だ。
そして、詳細は重要でないとなる。
かなり過激な説明のように感じる。
だが、そもそもソフトウェアって何か?アーキテクチャとは?に立ち返って考えてみる。
たとえば、この世界にRDBが無くドキュメントDBしかなくなるとか、.NETは終了しました、とかもう全ブラウザは今日で死にますとかないけどあったとしよう。
そもそものスタートはビジネスの要求であり、それを実現する手段がRDBなのか、ウェブなのか、どのフレームワーク使うのかは「詳細」だ。
ビジネスドメイン=方針が最重要で、それ以外の「詳細」はあとから決める、取り換えられる、延期できる。そういう形状にしなさいよ、アーキテクチャは。
そういう話しだと理解したよ。
ちょっと流れからは反れるが、なるほどの箇所があったのでメモ。
気をつけてほしい。反射的に重複を排除する罪を犯してはいけない。その重複が本物かどうかを見極めるべきだ。 — P163 第16章 独立性
重複しているから、と半ば機械的に集約しそうになる。
そういう教育を受けた気がする。
が、重複している箇所があったとしても、あくまでアーキテクチャの観点から考えてみてどうかを考える必要があるという。なるほど。
テスト境界について
先ほどの下りのあと、あの有名な図が登場し「クリーンなアーキテクチャはこれだ」バーンとなる。 この部分は素晴らしい解説のサイトがいっぱいあるので割愛。
その中の、テストについての説明が刺さったのでメモ。
境界はテストしにくい部分とテストしやすい部分に分割する。アーキテクチャの境界でこのパータンを使用すると、システム全体のテスト容易性が大幅に向上する。 — P210 第23章 プレゼンターとHumble Object
共通のシステムコンポーネントを変更すると、何百や何千というテストが壊れる可能性がある。これは、脆弱なテストの問題(Fragile Tests Problem)と呼ばれている。 — P238 第28章 テスト境界
ソフトウェア設計の第一のルールは、その理由がテスト容易性だろうと何だろうと、常に同じである。それは、変化しやすいものに依存しないだ。 — P239 第28章 テスト境界
たとえテストであっても設計原則は変わらない、ということだ。
言われてみればそりゃそうだ。
「本体の修正は容易に可能だが、テストがほぼ完全崩壊するためリリースは来年で」とかなったら意味ないもんな。
TDDと言えるほど立派なものではないが、とりあえずテストファーストでコーディングしている。
テストも同じ設計原則で組むといいよという話しはタイムリーでためになった。
最後に
正直、「100%完全消化」という状態ではないのでためになる解説を書いたりはできず、ほんと個人の備忘録ノートの感じですまない。
消化できていない部分は、経験不足。つまり、そこを本当に必要とする壁に当たっていない、がゆえに読んで頭でわかっても習得感を得られなかった、なのだろうな。
アーキテクチャとは関係ないけど、著者の文章がすごいなーと感動すら覚える。
自分が言うのはおこがましいが、本当に深く正しく理解しているからこんなにシンプルに説明しきれてしまうんだろうな、スッゲーな、かっけーなと思う箇所が多々ある。
手元においといて、きっと何ども読むんだろうね。