読書ノート『Clean Architecture』その1
自らアーキテクトを名乗るなら、あるいはやんごとなき事情(専ら他にそのレベルの設計できる人がいないという事情だろうが)でアーキテクチャを設計する羽目になったのなら是非とも一読しておきたい一冊である。
自分語り、及び動機
私自身、フロントエンドからバックエンドまでそれなりにコーディングすることが出来る所謂フルスタックエンジニア(これは私にとって不名誉な称号だ)であるが、開発初期フェーズのアーキテクチャ設計に於いては専ら声がかかることが多かった。
ただアーキテクチャ設計にあたって『コレは嫌な依存だな・・・』とか『ここはライフサイクルが違うから疎結合にしておいたほうがいいだろう』みたいな勘所による設計をしてしまっていたのは事実である。
アーキテクトとしてはもちろん、エンジニアとしてコレは良くないだろうと思っていた。エンジニアというのは原理原則を理解して、分解&再構築するのすることで金を練りだすのが本業であるからだ。
本書はDDD本と一緒に買って積んだままにしていたのだが(私の本棚も常時フルスタックで、なんならオーバーフローしている)、ふと思い出して読み始めたわけである。
オススメの読者
アーキテクチャ設計に携わっている、もしくは今後携わりたい人。
ソフトウェアの設計に関心を持ち始めた段階の人にとっては上位の原則は意味不明かもしれないが、とても興味深い内容になるだろう。
より良いソフトウェアを作ろうと思っているが詳細なIF文や関数の分け方以上に何かが根本的におかしいんじゃないかと思っている人は読んで見る価値がある。
この本はその根本的な何かについて書かれている。
本書の構成
私は科学や技術に関する本を読むときは、まず目次を読むことにしている。良い本は章立てが良く構造化されていて全体として洗練された論理構造を生み出している。
流石にアーキテクチャに関して記載しているだけあって本書の構造は洗練されている。全7部34章構成で、2〜6部はそれぞれの粒度で満たすべき原則が書かれている。1部はイントロダクションで7部は付録だ。
本稿も部単位で考察と感想を書かせてもらう。
第I部:イントロダクション
この部はアーキテクチャとはなにか、これをより良い状態に保つ必要性について書かれている。いわゆる動機だ。
無秩序に増改築を繰り返していくと改修コストは指数的に増えていく、という現象はシステム開発に携わったことがある人は誰でも経験しているはずだ。
技術的負債とか言われたりもする、膨れ上がる謎のコスト。
直感的には線形に増えていきそうなものだが、機能の数が組み合わせ爆発を起こすことを考えればこの指数発散は当然の帰結と言えるだろう。
この爆発するコストを抑え込み最小化することがソフトウェア・アーキテクチャの目的であると本書では述べれれている。
ソフトウェアは作ってしまって終わりではなく、必ず変更がかかる。
だからエンジニアは機能だけでなくアーキテクチャに関しても責任を持つべきという話である。
しばしば良いコードに関する議論が勃発するが、プログラムは動けばいいという話が毎度出てくる。それはソフトウェア・エンジニアとして正しい態度ではないと明言しているのだ。
仕様を満たしているかどうか、つまり機能に関してのみにスポットを当てて納品されたシステムがどれだけ酷い構造になっているかはSIerで働いている人のほうが詳しいだろう。
受注側は受入テストさえパスすれば報酬を得られて万々歳かもしれないが、将来的に構造に関する欠陥が元で莫大な改修コストを支払ったりシステム障害を起こすのは顧客である。
先のみずほの大規模障害などを鑑みると世の中は思ったよりアーキテクチャ起因の問題を抱えているのかもしれない。
第Ⅱ部:プログラミングパラダイムに関して
パラダイムとして以下の3つを上げている。
・構造化プログラミング
・オブジェクト指向プログラミング
・関数型プログラミング
これは一例ではなく全てらしい。
そしてこの3つがアーキテクチャの主要な関心事と対応している。
1つ目、『構造化プログラミング』は無秩序なGOTOを禁止し、『順次』『選択』『反復』の3つによりプログラムを構造化する手法だ。
アセンブラを読んだことががある人は最終的にはJUMP命令にデコードされることは知っているだろうが、制御の以降の仕方を3パターンに制限することで秩序を与えている。
これの制約を全体に適用することでプログラム全体を再帰的に構造化されて、数学や科学の分野で実績のある分割統治法が使えるようになる。
より小さなブロックは分離可能で『コンポーネントの分離』が可能になる。
私が思うに人類最強の道具の一つは再帰的構造である。これにより莫大な情報を対数的に丸め込み人間の貧弱な演算性能でも全体を制御することが出来る。
2つ目、『オブジェクト指向プログラミング』は無秩序な関数ポインタの仕様を禁止して、安全にポリモーフィズムを実現する手法だ。
オブジェクト指向とはなにかという議論は絶えないが、少なくとも本書の関心としてはポリモーフィズムの実現こそが本質である。
通常上位モジュールは下位モジュールに依存するが、インターフェイスを規定して処理を関数ポインタ経由でコールバックしてもらうことで依存関係を逆転させて下位モジュールをプラグイン化することが出来る。
インターフェイスの定義と関数ポインタ呼び出しをうまい感じにパッケージ化して隠蔽してくれているのがオブジェクト指向プログラミングというわけだ。
これにより自由自在に依存関係をコントロールできるわけだが、そのせいで依存関係が滅茶苦茶の滅茶苦茶になっているコードをしばしば見かける。
依存関係制御は非常に強力な機能だが、それ故にきちんと依存関係を設計して使わないと大変なことになるので気をつけたい。(そのためのクラス図だろう・・・)
ちなみに私はクラス図よりパッケージレベルの依存関係を書くことのほうが多い。この依存関係がきれいな単方向グラフになるようにリファクタをかけると大体コードの見通しが良くなる。
3つ目、『関数型プログラミング』は代入を制限することで変数の不変性を保証している。
変数の不変性が何故重要かと言うと、可変変数の数だけ状態の組み合わせが増えるからだ。ステートレス・ステートフルの議論も同じところに落着するだろう。
多数の変数の変動を常時把握し続けるほど人間の脳のメモリは大きくない。
そのスコープに於いて認識しておかないければいけないパラメータなど少なければ少ないほど良いのだ。
最近のlinterではローカル変数の再代入に警告を出すものがいるし、readonlyを宣言出来る言語も多いのはそれがプログラムの見通しを良くするからだ。
二度と変更されないなら変数を追うときは初期化している箇所だけ関心を払えばいいし、その変数が一度しか使われないならネストすればローカル変数はいらなくなる。
オブジェクト指向においてもインスタンス変数をコンストラクタで一度のみ初期化することにすれば、インスタンスの振る舞いの不変性は保証されるのでずっと見通しが立てやすくなる。
実践していると分かるのだが、上手いこと関数やメソッドを切り分ければローカル変数は殆どいらなくなり、ロックやカウンター制御と行った特殊な場合に限られてくる。
モジュールレベルでもシステムステートが様々な場所に散っているのは本当に認識不可がかかるので、自分は極力集約してステートレスなモジュールを可能な限り増やす。
ビジネスロジック上どうしても必要なステートというものは存在するので、すべての状態を追い出すことはできないのだが、ステートを考慮すべきスコープが絞られると言うのは大変ありがたい。
まとめ
長くなったので一旦ここで切る。
1部の内容は非常に基本的な原理原則で、情報論的にそれはそうだよね的な話であり、どこかで聞いたような話の総まとめ感はある。
それなりの経歴を積んだエンジニアなら肌感で気持ち悪さを感じるのはこの辺じゃなかろうか?
原理的で有るがゆえにコード一行一行からシステム全体まで全てのスコープで適用可能なものなので、ここの部だけでも全てのプログラマに読んでほしいところである。
この記事が参加している募集
この記事が気に入ったらサポートをしてみませんか?