「アーキテクチャのルールはどれも同じである!(ドヤっ)」
数々の書籍やアジャイルソフトウェア開発宣言、SOLID原則の提唱などで業界では有名なアンクル・ボブ(Uncle Bob)
ことロバート・C・マーチンさんによる、よりよいソフトウェア・アーキテクチャと設計の追求の本。原著が2017年、翻訳が2018/8、その後ITエンジニア界隈でもかなり話題になりました。
実は去年一度読み始めたのですが、AWS認定を突破する!と決意したので例のタマネギ(あるいはドーナツ)にたどり着く前に中断。無事に3冠突破して戻ってきたので、今年の夏に改めてじっくりと最初から読むことができました。
アーキテクチャのルールはどれも同じである!という帯の煽りは極端ですが、要はコンピュータやエンジニアリングの進化の中で発見されてきた、時代を超えて通用する不変のルールもある、これらをアーキテクチャの観点から見ていこうという本です。
以下、未来の自分が振り返る時やこれから読む方への恩送りのために、読書メモ&感想&自分なりに噛み砕いた理解を、自分の言葉で書いていこうと思います。
(既に本書を読破した方や実践している方、たぶん解釈違いもあるかと思いますがご容赦ください。)
- 「アーキテクチャのルールはどれも同じである!(ドヤっ)」
- 第I部 イントロダクション
- 第II部 構成要素から始めよ:プログラミングパラダイム
- III部 設計の原則
- 第IV部 コンポーネントの原則
- 第V部 アーキテクチャ
- 第VI部 詳細
- 第VII部 付録
- まとめ:クリーンでより良いアーキテクチャ設計と、The Clean Architecture、(+昔話)が学べる本
- ボブおじさんのクリーン本シリーズなど
Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)
- 作者:Robert C.Martin,角 征典,高木 正弘
- 発売日: 2018/08/01
- メディア: Kindle版
第I部 イントロダクション
第1章 設計とアーキテクチャ
設計とアーキテクチャではアーキテクチャの方が上位に置かれることが多いが、本質的には両者は同じである。実際の開発の事例のショッキングなグラフを元に、単に急いで作るよりもきちんと設計してクリーンなコードを書き、うまく進んでいかないと後で大変なことになるよ……という話。
第2章 2つの価値のお話
完璧に動くが変更できないプログラムよりも、完璧でなくても変更が簡単なプログラムの方が価値がある。ふるまいよりもアーキテクチャを考えていこう……という話。
変更に強い設計とコードを目指そうというのは、アジャイル的な文脈でも普通の開発でもよく語られますね。
第II部 構成要素から始めよ:プログラミングパラダイム
第3章 パラダイムの概要
プログラミングの歴史の中で今までにあった大きな変化では……
- 構造化プログラミングによって「直接的な制御の移行」に規律が課せられた。
- オブジェクト指向プログラミングによって、「間接的な制御の移行」に規律が課せられた。
- 関数型プログラミングによって、「代入」に規律が課せられた。
とばっさりと結論付けて細部を述べていきます。
第4章 構造化プログラミング
遡って1960年代の大昔、プログラムで機能を分割することが重要でGOTO文は有害だという重大な発見をした人のお話。これによってプログラム内を自由に移動できてしまう「直接的な制御の移行」はいらないというのが分かり、その後生まれた新しいプログラム言語ではGOTOはなくなっていきます。
政府のGoToキャンペーンのハッシュタグがTwitterに流れていた頃、エンジニア界隈の一部の人がこのネタで盛り上がっていたのを思い出します。
ちなみに僕はエンジニアとしてのキャリアの一番最初の半年は銀行のシステム開発だったのですが、仕事で最初に触った言語であるPL/IはまだGOTO文あったんですよね……90年代終わりの話です。
第5章 オブジェクト指向プログラミング
オブジェクト指向の良いところとして上げられるカプセル化、継承、ポリモーフィズムの3つは、別にいにしえから続くC言語でもできるぜ……と若干ドヤってからの、ポリモーフィズムの正しい活用がオブジェクト指向(OO)のパワーだ!(ドヤ!)という話。
- ふつうは上位レベルの関数が下位レベルの関数を呼び出すので、上位→下位に依存。
- しかし上位レベルの関数がインターフェース経由で呼び出すと、下位レベルの関数→インターフェースの方向に依存の方向が変わり、依存性が逆転する!(ドヤ!)
けっこう主張を強めに言い切っているので、悪く言ってしまうと老害の昔話感も若干ありますが(笑)、本章に限らず歴史に基づきそこから導き出された洞察はなかなか興味深いです。
第6章 関数型プログラミング
仕組みの中にある可変コンポーネントでなく不変コンポーネントをなるべく使うと変数を不変にできる。
仮想の話だがデータの保存場所がもしも無限にあれば、例えば銀行の大量の取引も全部新規保存するだけで上書きしない「イベントソーシング」の考え方が実現でき、関数型プログラミングになる。
構造化/オブジェクト指向/関数型の3つのパラダイムは「何をすべきでないか」を教えてくれた。つまりソフトウェアの本質は半世紀経った今も変わっていないのだ(ドヤ!)というお話。
III部 設計の原則
イラストでチアガールがSOLIDのカードを掲げていますが、アメリカンな絵なのでまったく可愛くn……もとい、よりよい設計の話の部。
変更に強く、理解しやすく、ソフトウェアの他の部分から再利用しやすい中間レベルのモジュールを作るのに役立つのがこれだよという、設計の話でときどき出てくるSOLID原則の話が始まります。これも長年の議論や考察を経て生まれたそうです。
第7章 SRP: 単一責任の原則
モジュール(クラス)の仕事はひとつであるべき……と理解してしまいがちですが、本当はモジュールを変更する理由はひとつだけであるべきである、そしてそのモジュールを使う1種類のユーザー(=アクター)に対してだけ責務を負うべきである、という話。
例に上がっているのは、経理と人事とDB管理、それぞれ仕事が違う人が使う大きな処理が、1つのクラスの中に混在しているという話。これは比較的分かりやすいですね。本書で解決策として挙がっているのは
- データ構造は共通にして、3つの独立したクラスで処理
- さらに入り口はFacadeクラスで共通にして、中の処理を3つの独立したクラスへ分岐
- 元の大きなクラスに重要な処理は残し、切り出せるところは別の独立したクラスへ移譲する
このへんはクラス設計やリファクタリングが分かる人なら馴染み深いと思います。
またこの原則は適用すると、最終的にはクラスの仕事は独立していてひとつになった……という結果に繋がる場合もあると思います。
第8章 OCP: オープン・クローズドの原則
ソフトウェアは拡張については「開き」、修正に対しては「閉じて」いなければならない。
という説明が抽象的ですが、拡張しやすく、かつ既存部分は拡張の影響を受けにくいのが良いですよという話。
例としては会計報告のデータをWeb出力、印刷の2パターンで出力するプログラムが上げられています。
複数クラスを集めた二重の四角が本書では「コンポーネント」と表現されていますが、実際のプログラム言語ではこれは、パッケージや名前空間でまとめられた幾つかのクラス群、という具合のイメージでも良いかと思います。
- コンポーネント間の矢印は常に一方通行で、向かう先が依存先。自分が変更されても影響を及ぼしたくない相手になる。
- ビジネスルールを扱うコンポーネントを最上位にするとよい。出力をどうするか、データの保存先などのコンポーネントは周辺的な関心事であり、下位レベルとして扱う。上位レベルは下位レベルの影響を受けない階層構造にする。
- 矢印が繋がっていてもその先のコンポーネント内部を知りすぎないように、インターフェースで保護して情報隠蔽する。
とかなり本格的になってきます。後から改造しても変更の影響を受けにくい頑丈なシステムにしましょうという話ですが、この原則を実現する実際の手段はその状況によって違ってくるなあというところ。
第9章 LSP:リスコフの置換原則
この原則は1988年に提唱したリスコフさんという偉い人の名前が入っています。
あるクラスがインターフェイス経由で別のクラスを呼び出したら、実際に処理するクラス(たとえば仕事免許クラスと個人免許クラスの2種類)を互いに入れ替えても影響がないなら、この置換原則を満たしているという話。
違反している例で載っているシステムが、タクシーの配車コマンドをRestfulなWebサービス経由で呼び出す……というのが今っぽいです。
Acmeという特別なタクシー会社だった時のみ、フィールドのフォーマットが微妙に違うのだがどうしようというケースが上げられています。
本書での解決策は、配車フォーマットを予め設定データベースに持たせ、例外的な分岐処理が増えたらこのデータベースに追加していくだけで済むようにするというもの。確かにプログラムにif文を追加していくよりは運用も楽でしょうか。
第10章 ISP:インターフェイス分離の原則
依存している呼ぶ先のクラスの処理を、インターフェース経由にすれば依存が少なくなるよという話。アーキテクチャのレベルでも依存は少ないほうが良いと述べています。
第11章 DIP:依存関係逆転の原則
変化しやすい具象クラスを直接参照したり継承したりせず、インターフェース経由にすると依存関係が具象側→抽象側になって矢印が逆転し、よくなるよという話。
デザインパターンのAbstract Factoryを使って具象クラスを作ると良いそうです。
第IV部 コンポーネントの原則
第12章 コンポーネント
本書ではコンポーネントは「デプロイの単位」と定義されています。1回のリリースで置き換えるようなあるパッケージのクラス群みたいな塊でしょうか。「HTMLコンポーネント」などとはちょっと意味が違いますね。また本書の後半に行くと、必ずしもデプロイの単位とは限らなさそうな、「処理のかたまり」「機能ごとのかたまり」的な意味合いで使われているところもあります。
実行時にコンポーネントを含められるようになるまで、コンピュータの進化には長い長い時間が必要だった……という昔話。
第13章 コンポーネントの凝縮性
- 再利用・リリース等価の原則:再利用の単位とリリースの単位は等しくなる。
- 閉鎖性共通の原則:同じ理由で、同じタイミングで変更されるクラス群がひとつのコンポーネントの中にまとまる。
- 全再利用の原則:使わないものには依存しない。
もう動いているアプリケーションに追加である機能をリリースするとしたら、全部がひとつのコンポーネント内にまとまっているのが理想。あちこちにごちゃごちゃ散らかっていたら、それは凝縮性が足りていないことになる……というイメージでしょうか。といってもパッケージ/名前空間がレイヤー毎に区切られていたらこうならないこともあると思います。
現在はよく出てくるマイクロサービスの話も、このへんの原則と繋がってきますね。
第14章 コンポーネントの結合
- 非循環依存関係の原則:依存の矢印は一方向でなければならない。矢印がぐるぐる回ってしまうときはインターフェース経由にするか、双方が依存する新しいコンポーネントを作ると良い。
- 安定依存の原則:不安定なコンポーネントから安定したコンポーネントの方向に依存するとよい。
- 安定度・抽象度等価の原則:安定度の高いコンポーネントは抽象度も高い。抽象度が高い方向に依存するとよい。
述べられているすべての原則にアルファベット3文字の略語が割り当てられていますが、正直覚えにくいです(笑)。
本文中にもありますが、まあ闇雲に全原則を適用しようと躍起になるのではなく、バランスよくというのが実際のところでしょうか。
第V部 アーキテクチャ
第15章 アーキテクチャとは?
アーキテクチャとは作った人がシステムに与えた形状であり、中はコンポーネントに別れていく。
よいアーキテクチャは開発・デプロイ・運用・保守をより容易にしていき、ライフサイクルをサポートし、開発者の生産性を最大化していく。できるだけ多く選択肢を残しておくのがよい…と各論に続いていきます。
「ソフトウェアアーキテクトは引き続き最高のプログラマであり、コードを書かないなんてのはデタラメだ!」
とボブおじさんがエモく叫んでいますが、これには強く同感です。
- 開発:チーム構成によって異なる。大人数複数チームだと、コンポーネント単位で分割するしかない。(=コンウェイの法則)
- デプロイ:単一のアクションで簡単にデプロイできるのが理想。早い段階から考えておくとよい。
- 運用:アーキテクチャが優れていれば、自ずと運用方法が導ける。
- 保守:主なコストは後から修正する時に修正箇所を探す「洞窟探検」にかかるコストと、間違っていじってしまうリスク。慎重に考えられたアーキテクチャは低コストで済む。
- 選択肢を残しておく:システムにはビジネスルールや手順を含む価値のある「方針」と、方針には影響を与えないDBやフレームワークやWebサーバープロトコルなどの「詳細」に分かれる。「詳細」は「方針」と関係なく決めることができ、決定を後回しにすればその分より多くの情報から正しい判断ができる。
抽象的なコンポーネントの箱の構成図で考える段階では、詳細までは踏み込まないということでしょうか。以前読んだ『Design It!』でも同じようなことを述べていました。
Design It! ―プログラマーのためのアーキテクティング入門
- 作者:Michael Keeling
- 発売日: 2019/11/25
- メディア: 単行本(ソフトカバー)
このアーキテクチャ例が載っているのですが、RESTやDIやAjaxやHTMLが出てくるのは普通として、ちょっと古いJSP(Javaでの画面作成の技術)やJSF(Java Server Faces, 2000年代にStrutsの後に来ると言われて来なかったWebフレームワーク)が出てきて、さすがに歴戦のボブおじさまという感じ。
DBやWebサーバーを後から変えられるように、変更に強い作りにするのは分かりますが、Webフレームワークの選択は開発の比較的初期で決めちゃって後から変えることはないんじゃないかな?と思います。このへんは後書きでも訳者の角征典さんが
フレームワークから着手しないことが本当に正しいことなのかは、私にはまだよくわからない。
と正直に感想を漏らしており、現実にそぐわないところもあるので適宜差し引いて考えた方がよいのでしょう。
実際の例として大昔、プログラムの出力先をカードリーダーから磁気テープに変えるのに苦労したりプリンタの種類を変えるのに成功した経験から、デバイス非依存のコードが良いという知恵にたどり着いた話があり、要は本書で繰り返し語られる「方針」と「詳細」をうまく分けると良いという話になっています。
第16章 独立性
- ユースケース:優れたアーキテクチャであれば、システムから振る舞いが自然と見える。
- 運用:コンポーネントを適切に分割していれば、後からマイクロサービス化したり移行がしやすくなる。
- 開発:チームがお互いに干渉しないように、それぞれ別のコンポーネントを担当する。(=コンウェイの法則)
- デプロイ:コンポーネントの適切な分離・分割が即時デプロイに繋がる。
- 選択肢を残しておく:言うは易しで現実的には難しいことも述べられています。要はバランス感覚。
- レイヤーの切り離し:だいたいはUI、ビジネスルール、DBなどの水平レイヤーで分離される。レイヤーの上にレイヤーが乗る形。
- ユースケースの切り離し:注文の追加と削除は別のユースケース。それぞれがUI→ビジネスルール→DBと続いていくようにレイヤーを薄く垂直に切る形になる。よくあるレイヤードアーキテクチャになる。
- 切り離し方式:コンポーネントをサービスレベルまで分けることもある。選択肢を残す事が大事。
- 独立した開発が可能:うまく切り離されていれば、別チームがそれぞれ別のユースケースを開発する時、互いに邪魔されない形になっているはず。
- 独立デプロイ可能性:うまく切り離されていれば、デプロイも個別になる。
- 重複:プログラミングのDRY原則で消したくなるが、中には偶然で重複しているように見えることもあるので注意。
- 切り離し方式(再び):クラスのレベルで分けるソースレベル、jarファイルやDLLで分けるデプロイレベル、サービスレベルがある。
最適な方式は初期段階では判断が難しいのだが、あとから変化する可能性を予見してうまくやっていくのがアーキテクトの腕の見せどころだと述べています。まあ結局バランス感覚で、その時の状況に応じてうまくやっていくしかない訳ですね。
第17章 バウンダリー:境界線を引く
早すぎる結合は開発者を不幸にする話を出して、重要なものと重要でないものの間に線を引く境界線の話。例としてここでも、
GUI | ビジネスルール | DB
の間に線が入っています。
クラス図の例が出てきますが、ビジネスルールからは常にデータベースのインターフェイス経由でアクセスすれば、実際のデータベースのクラスは意識せずに済む。DB→ビジネスルールの方向のみの依存になり、実際のDBの種類がなんであろうと関係ない…となります。これは多くのWebアプリケーションフレームワークが実装しているので、経験のある方には理解できると思います。
同じようにGUIについても線を引いて、UIの種類を後から変えてもビジネスルールからは関係なくするのが良いと。
この
UI → ビジネスルール ← DB
の中央へだけ向かう依存関係は本書全体を通して共通ですね。この基本構造が最後のThe Clean Architectureに繋がっていきます。
第18章 境界の解剖学
ここも境界線から続く話です。クライアントからサービスを呼び出す際、サービスのインターフェース経由で呼べばサービスの実装クラスは気にしなくて済む。レイヤー間で引いた線引きがデプロイに役立つ…という話。
ほか、ひとつのマシン上でローカルプロセス同士が通信する場合、ネットワークを介してサービス同士が通信する場合、これらも境界線になるという話。
第19章 方針とレベル
以前出てきた「方針」ごとにコンポーネントを作る。コンポーネントには「レベル」があり、依存は常には下のレベル→上のレベルへだけであるべき、という話。
例に挙げられているのは暗号化プログラム。暗号化の処理自体が上位レベルのコンポーネント、そこに依存する下位レベルが文字の入力機能と文字の出力機能だというもの。
この例は分かりやすいですね。この話も最後のThe Clean Architectureに繋がります。
第20章 ビジネスルール
利益を生み出したりするビジネスの決まり、ローンの利子計算などを「ビジネスルール」と定義、そこで使われるのを「ビジネスデータ」と定義。
そして、こうしたビジネスデータを介してビジネスルールを処理するオブジェクト(≒クラス、コンポーネント)の塊を「エンティティ」と本書では定義しています。コンピュータの内部にあり最重要で、DBや画面やフレームワークや特定技術に依存しない独立したもの。
なおエンティティ、Entity
という言葉はこういうアーキテクチャ周りの文脈では、データベースにアクセスするクラスなど別の意味で使ったりすることも割とあるので、区別が必要ですね。
そしてビジネスルールをどの順でどう呼び出していくか、アプリケーション固有のルールを示しているのが「ユースケース」。ユースケースも入力と出力はあるものの画面やDBとは関係しない独立したもの。
オブジェクト指向が流行りだした頃に設計技法でUMLを学んだ身としては、記法の「ユースケース図」を思い出してしまうのですが本書の文脈ではちょっと違う意味。
ユースケースとビジネスルールとの差が分かりにくかったのですが、ビジネスルールをくるむ一つ外側のレイヤー、個々のビジネスルールを順次呼び出していくレイヤーという感じで理解しました。
第21章 叫ぶアーキテクチャ
アーキテクチャは本来の目的のユースケースについて叫ぶべきだ! フレームワークは便利だがあくまでツールであり、支配されてはいけない、アーキテクチャとは別のものだ!とひときわエモ度高くシャウトしている章。
なんとなくですがダニング・クルーガー曲線の「完全に理解した」状態にある駆け出しエンジニアに、「本質を理解しないうちからRailsこそ世界最強とかそういうイキッたことを言うな!」とかそういう話を語っているようにも見えます。(笑) (もちろん、Railsを活用したり学んだりしている方を侮辱する意図はございません。 )
Railsを使ったサービスといえばしばらく前、noteで投稿者のIPアドレスが流出するという事故が話題になりました。Rails本では有名な『パーフェクトRuby on Rails』の著者の方が、問題になりそうな点と認証ライブラリの使い方を解説されています。
これがとても丁寧に解説されていて、仕事でRailsを使っていない自分でも学びになりました。Railsは開発速度と引き換えにRDBと密に結合しているフルスタックなフレームワークでもあるので、この種の問題が起こりうるのですね。
この手の話と合わせて考えると、本書Clean Architectureで度々述べられている、コンポーネントを分離独立させること、レイヤーごとの境界線を明確にすること、ライブラリなりフレームワークなりに依存しすぎないこと……というのも頷ける話でもあります。
第22章 クリーンアーキテクチャ
これまで述べてきた、より良いアーキテクチャの追求をボブおじさんなりに実現した一例として、例のタマネギ(あるいはドーナツ)図が有名な『The Clean Architecture』
と名前が付いたアーキテクチャパターンがやっと登場します。
フレームワーク/UI/DB/外部のインターフェースに依存しない、ビジネスルールの部分はテスト可能である、依存は常に外側から内側の一方向である、境界線を越える際はインターフェースを経由してアクセス……と、本書で語られてきた原則や技法がそのまま体現されています。
レイヤー構成は以下となっています。
- エンティティ(
Entities
):最重要のビジネスルール部分のレイヤー。外側からの影響を受けない、独立してテスト可能。 - ユースケース(
Use Cases
):その外側、アプリケーション固有のビジネスルール部分のレイヤー。 - インターフェースアダプター(
Interface Adaptors
):その外側とのやりとりでデータを変換するレイヤー。プレゼンター/ゲートウェイ/コントローラーに分かれる。画面の検索条件→データを入れるクラスに変換、とか検索結果の配列やリスト→画面に埋め込む などでしょうか。 - フレームワークとドライバ(
Frameworks & Drivers
):一番外側。ここをフレームワークと捉えると分かりにくいですが、各種DBやUIの仕組み、スマホの画面のイメージで捉えると分かりやすいかと思います。
自分も最初ピンとこなかったのですが、次に典型的っぽいWebシステムの例が載っていたのでやっと理解できました。
従来のMVCアーキテクチャで例えるとだいたい以下の対応でしょうか。
MVCアーキ | The Clean Architectureでの層 | The Clean Architectureでの処理 |
---|---|---|
画面からのリクエスト→Controller | インターフェースアダプター層 | コントローラーが受け取って処理 |
Controller→Modelのあたり | ユースケース層 | InputDataを通してデータが移動、例ではユースケースインターアクターとなっている部分が処理 |
Model | ユースケース層からエンティティ層 | 内側を呼んで最重要のビジネスルールを処理 |
Model | ユースケース層から外→外へ | データアクセスインターフェース経由でDBを呼んで処理(必要に応じデータをエンティティ層にも渡す) |
Model | ユースケース層 | 処理が終わったらOutputDataにデータを詰めて外側に渡す |
Controller | インターフェースアダプター層 | 今度はコントローラーでなくプレゼンターが処理、ViewModelにデータを詰めて外側に渡す |
View | フレームワークとドライバー層 | ビューが画面を描画して終わり。 |
- MVCのController層に当たるのがインターフェースアダプター層で、入り口のControllerと出口のPresenterに分かれる
- MVCのModel層でDB処理以外の事柄がユースケース層とエンティティ層に分割され、より細分化されている
- ビジネスルール処理と密接に繋がりがちだったDBが、一番外側に隔離、分離独立入れ替え可能状態になっている
あたりが違う所となります。
有名なタマネギ(あるいはドーナツ)のアーキテクチャ図ですが、一番外側の層にフレームワークが配置されるのがやっぱりイメージしづらいですね。
スマホアプリやUnityアプリでなくWebアプリ開発の場合、サーバーサイドのWebアプリケーションフレームワーク、特に多機能でフルスタックなもの(RailsとかLaravelとかDjangoとかSpring MVC/Spring BootとかASP.NET MVCとか)は除外して考えて、もっと単機能のライブラリなどをここにぽつんと置いて、より内側の層は外側のこれに依存していない、すぐ入れ替え可能なようにしてある構成……というぐらいのイメージでいた方が理解しやすいかと思います。
フロントエンドの技術の場合は、ReactやVue.jsだったら画面を描画する薄いコンポーネント(本書の定義でのコンポーネントでなくそれぞれの技術でのコンポーネント)をこのフレームワーク&ドライバ層に配置、サーバーと通信したり演算したりするロジックがより内側の層で、そこから単純なデータ構造で外側に渡す……ようなイメージでしょうか。
なおこの『The Clean Architecture』は元はブログ記事で、そこからのこの本に発展しています。日本語訳もされています。
第23章 プレゼンターとHumble Object
テストがしにくいふるまいをテストしやすい部分とテストしにくい部分に分ければテストが可能になる、このテストしにくい部分をHumble Object
(ハンブルオブジェクト)と呼ぶ……というデザインパターンの話。難解なことで知られる書籍『エンタープライズ アプリケーションアーキテクチャパターン』(PofEAA)の名が出てきます。
エンタープライズ アプリケーションアーキテクチャパターン (Object Oriented SELECTION)
- 作者:マーチン・ファウラー
- 発売日: 2005/04/21
- メディア: 大型本
パッと思いついた、よくありそうなWebフレームワークに当てはめると以下でしょうか。
The Clean Architecture | MVCアーキテクチャのフレームワーク群での対応 |
---|---|
Presenter | Controller層に相当。テストクラスでテスト可能。 |
ViewModel | Controllerから画面のHTMLに相当する仕組みに渡す時専用のデータの入れ物クラスや連想配列に相当。ここもテスト可能。 |
View | HTMLを表示する仕組み。PHPならBladeやSmartyなどのHTMLテンプレートエンジン、JavaならJSPとかThymeLeafとか、C#ならaspxとか。ここ自体はテスト不可になる。 |
本書に出てくるViewModel
は、フロントエンドのフレームワーク群などの文脈で語られるMVVMアーキテクチャパターンに出てくるVM(Model-View-ViewModel
でこちらもViewModel
)とは同名でもちょっと違う話なので、ここは注意ですね。
まとめにあるように、境界を超えるデータの移動には極力シンプルなデータ構造を使うと、システム全体でテストがしやすくなるという話だと理解しました。
第24章 部分的な境界
Clean Architectureがデデーンと出た後は、ちょっと脇にそれた話が続きます。
コンポーネントの境界をきっちり分けるのが大変な場合、一部だけやるにはどうしたらいいかの章。
- 最後のステップを省略する:コンポーネントを分けられるようにしておくが、デプロイ時はひとつのコンポーネントにしておく。アプリケーションが成長した後になると分けるのが難しくなってくる可能性あり。
- 片方だけの境界:一方向だけ、インターフェースを継承した実装クラスを用意してインターフェース経由のアクセスにする
- Facade:デザインパターンの有名なFacadeパターンで、入り口クラスから細分化したメソッドでサービスを呼んでいく。
第25章 レイヤーと境界
アメリカでは歴史的には有名らしいテキストベースのアドベンチャーゲーム Hunt the Wumpus
を題材に、レイヤーの境界分けの話。レトロゲーみたいなもののようです。
- ゲームルールが本体なので最上位、他から依存されるだけのコンポーネント。
- より下位には言語のコンポーネント。まずインターフェースがあって実装クラスが英語やスペイン語など複数。
- テキスト入力のコンポーネントも、まずインターフェースがあって実装がコンソール入力やSMSなど複数。
- データの置き場所のコンポーネントも、まずインターフェスがあって実装がパソコン内の場合もクラウドも。
では簡単なプログラムで書けそうなこのゲームが、もしもMMORPGのように本体がネットワーク上にあったら?この場合プレイヤー管理のコンポーネント、パソコン上の移動管理のコンポーネントが別になってここが境界になるよ...と、どんどんコンポーネント構成図が複雑になっていきます。
簡単なシステムであればわざわざ境界を作らずとも実現できる。しかし成長して複雑になっていくと必ず境界が必要になるので、ソフトウェアアーキテクトは常に注意深く見張る必要があるよ...という話でした。
第26章 メインコンポーネント
他のコンポーネントを呼び出したり設定を持ったりする最初のエントリポイントのコンポーネントが必ずあり、ボブおじさんはこれをMain
コンポーネントと呼んでいるそうです。一番下位、クリーンアーキテクチャの一番外側の円にあるべきもの。
前章のゲームについてのJavaでの実装例が書いてありますが、このMainクラスが各種設定を持ち、public static void main ...
なメソッドでマップを作って処理開始、キー入力を受け付けて実処理は別のクラスに任せて…とやっています。まさに入り口の処理開始クラスですね。
このクラスをプラグイン的に扱って、開発やテスト用で分けたりすると便利だよというのは理解できました。
第27章 サービス:あらゆる存在
サービス指向アーキテクチャ(SOA)、マイクロサービスアーキテクチャ(MSA)について疑問を投げかける章。ボブおじさんはどうやらマイクロサービス否定派まではいかないけど全肯定ではないようです。
まずサービスを分けると分離して便利になると言われるが、データを共有してるとそれは分離とはいえない。またスケーラブルなシステムを作るには必ずしもサービスで分ける必要はない。
そして実例として、マイクロサービス指向で考えたタクシー配車システムが出てきます。人がスマホのUIから敗者を申し込むと各サービスが連携してタクシーを探し、タクシー会社に連絡が行って車が来てくれるもの。
このサービスに、子猫宅配サービスという人類に幸せをもたらしそうな機能追加がされたらどうなるか。全マイクロサービスに変更が入ってしまうではないか!(ドヤっ)
ということでその昔「アスペクト指向プログラミング」が流行った頃に現れた「横断的関心事(cross-cutting concerns)
」のワードと共に、こうした変更に弱いことを指摘しています。
ボブおじさんの解法はモノシリックなアプリケーションを例にとり、乗車の機能と子猫宅配の機能は別コンポーネントにしてFactory
パターンで振り分け。Template Method
とStrategy
パターンを使ってうまく他の機能に当てはめる…というもの。オブジェクト指向プログラミングとデザインパターンをうまく使った解法です。
Javaの場合であれば、子猫宅配の新機能分は新しい派生クラス分を別のjarファイルにして各サービスに配ればよい。マイクロサービスの場合でも実現できるか……?→からの、「もちろん!」(ドヤっ)と自信満々です。
サービスの境界線はサービスとサービスの間になり、一見アーキテクチャの境界線も同じように思える。 しかしアーキテクチャの境界線はまったく別の話で、複数のサービスを横断することもある。各サービス内部のコンポーネントによって決まるのだ……というのが要点だと読み取りました。
第28章 テスト境界
テスト容易性を保った設計が大事。UIのような変化しやすいものには依存させない…という話。 ビジネスルール部分をテストできるテストAPIを用意するとよいとのことでしたが、具体的な例がなくてこの章はよく理解できませんでした。
第29章 クリーン組込みアーキテクチャ
ソフトウェアとハードウェアの間に「ファームウェア」のレイヤーがあり、なかなか完全な独立性をもたせられない組込み開発で、いかにクリーンなアーキテクチャを目指す話。
スマホ開発をやっていないので自分には細部は分からない所もありましたが、Webに比べるとうまく切り離すのが難しいのだろうなあというのは想像がつきます。
本書ではソフトウェアとファームウェアの境界線はうまく定義できないので、
ソフトウェア→(OS抽象化レイヤー)→OS→(ハードウェア抽象化レイヤー)→ファームウェア→ハードウェア
のように中間層を持たせると良いと論じています。
第VI部 詳細
第30章 データベースは詳細
タイトル通り、The Clean Architecture流の考えではデータベースは方針でなく詳細、些細なことの範疇に入るぞという話。
例によって昔話に入りますが、データをディスクに保管していた時代は性能がものすごく遅く、RDBはそこが凄かった。今後もデータの格納場所は別の形に進化するかもしれないし、プログラマーの視点からすると詳細で扱うべきだと論じています。
1980年代の小話で、データはシンプルにファイルで保存するだけでよいところ、マーケティングの人がRDBMS導入を声高に主張して若き日のボブおぢさんが戦い、結局負けて逃げ出して転職した……という話が最後に載っています。
あなたのそんな話聞いてないがな!w とちょっと笑ってしまいましたが、技術のことが分かっていない営業との軋轢というのは今でもありますからねえ。こういう長いキャリアの中での様々な経験や苦難が、原則を忠実に守るクラフトマンシップ魂の源泉となっているのだろうなあと思います。
第31章 ウェブは詳細
最初はIT技術の変遷で時折語られる、ずっと続く「振り子」の話から。 Webアプリケーション関連で言うと、
- 最初はサーバーですべて処理、ブラウザは表示するだけ。PerlのCGIやApacleなど。
- →ブラウザ上で動かすJava AppletやFLASHのような技術が登場する
- →でもやっぱりサーバーサイドがメイン
- →Web2.0とGoogle Map、Ajaxが衝撃を与え、クライアントサイドが力を増す
- →さらにNode.js登場
ここでボブおぢさんは
「JavaScriptをサーバー側に戻すのが流行っている。(やれやれ)」
とまた主流がサーバーサイドに戻ったようにも?取れる書き方をしています。しかしここは、Node.jsの登場でサーバーサイドの言語の選択肢にJavaScriptも加わりサーバーサイドレンダリングなんかも出てきたが、クライアントサイド(フロントエンド)が力を増し続けるのは変わらない……ぐらいの方が正しいかなと思います。
WebもGUIであり、入出力デバイスの一種である。アーキテクトとしては長期的な視点で考える必要があり、ビジネスルールとは切り離して詳細として抽象化して考えるべきだ…というのは分かるところ。
ここでも例によってまた昔の小話が出てきて、マーケティングの連中の主張でアプリのGUIの見た目を大幅に変更し、結局失敗してしまった会社の話が出てきます。ボブおぢさんあんたマーケティングに恨みあるんかい!w と笑ってしまいますが、キャリアの長い方だとこういうシーンはあるでしょうねえ。
「マーケティングの奴らはいつも、ビジネスルールとUIを一体化したがるのだから。」
というのは技術者の立場からするとなんとなく気持ちは分かります。
本の内容から離れて余談になりますが、このWebがサーバーとクライアントを行ったり来たりする話、静的型付け言語と動的型付け言語の間を行ったり来ている話、コンピューティングの進化が集中と分散を繰り返している話、技術の規約が厳しいと緩いのを間を行ったり来たりしている話……などいくつかの話題は、まるで「振り子」のようだ、とよくIT界全体を俯瞰した文脈で話題になります。
しかし同じことを繰り返しているわけではなく進化しているわけで、振り子でなく「螺旋」と捉えてその差分に着目して未来に備えよう……というのが、TDDで有名なtwadaさんの名スライド『技術選定の審美眼』。2018、2019年のデブサミで大きく話題になりました。
資料はキーワードの列挙までが主なので推測したりするしかないのですが、技術系Podcastでおなじみiwashiさんの『fukabori.fm』でゲスト出演された際に、ご本人から詳しく解説が入って深掘りされています。
講演を聞けなくてもスライドの内容が補完できて学びが多く、この回はとてもオススメです!
第32章 フレームワークは詳細
フレームワークはアーキテクチャではない。必要以上に結合しすぎるのは一方的な結婚のようなものでリスクとなる、独立性を保て!とこちらもエモ度高めにシャウトしている章。
本書のあちこちで語られていますが、The Clean Architectureのドーナツの真ん中、最重要のエンティティの層はDBやWebから独立させるのと同様に、フレームワークからも完全に独立させよと述べています。
各レイヤー毎などで、フレームワーク側が準備した親クラスを継承して派生クラスにロジックを書くとフレームワークの機能も使えるようになって便利に……なんてのはよくある標準的な使い方ですが、The Clean Architecture流の最重要レイヤーであるエンティティ層にはこれをするなとのこと。
Javaの代表的FWであるSpringが引き合いに出されていますが、依存性の注入ができる @autowired
のアノテーションはビジネスオブジェクトでなく、一番外側のMainに対して行うとよいと論じています。
ボブおぢさんはフレームワーク否定派とは言わないまでも、あまり深入りしない派のようですね。
なんだか「軽はずみに結婚すると尻に敷かれるからやめておけ!」と酒の席の勢いで若い男子に説教もとい、人生の教訓を語っちゃってるおじいさんのような感もあります。
第33章 事例:動画販売サイト
チュートリアル動画を販売するサイト、今だったらUdemyのようなサイトを例にとったアーキテクチャ設計の実例。
ユースケース図に登場する作者/管理者/購入者/視聴者ごとに、
Controller→Use Case Interactor→それてDBのGateways→戻ってPresenter→View
と流れる形で、コンポーネントが整然と分割されています。
第34章 書き残したこと
ここはSimon Brownさんという別の方に変わって、Clean Architectureからは一旦離れてパッケージングの話。 ここでいうパッケージングはJavaならpackage、C#やPHPなら名前空間、Rubyならmodule、PythonやJSも名前空間のことで、どうクラスをまとめていくかの話が整理されています。
- レイヤー毎にパッケージ:AbcController→AbcServiceを継承したAbcServiceImpl→AbcRepositoryを継承した各DBクラス の流れで、
web, service, data(database?)
のような名前の3レイヤー毎のかたまりに分ける。一番よくある方法。 - 機能によるパッケージ:注文機能だったらそれに関する各3レイヤーのクラスを1パッケージに。まとまっているので修正箇所を見つけやすい。
- ポートとアダプター:The Clean Architecture風に内側の円のドメイン層、外側のインフラストラクチャ層で塊を分ける。AbcControllerが外側の円でひとかたまり、AbcServiceとAbcServiceImplとAbcクラスが内側の円のドメインでひとかたまり、DBにアクセスするクラスが外側の円でひとかたまり。
web, domain, database
のような名前のコンポーネントが3つになる。 - コンポーネントによるパッケージング:入力のAbcControllerがひとかたまり。その先はぜんぶまとめて「コンポーネント」のかたまりで合計2つ。publicのアクセス修飾子を限定すれば、Controllerから直でDBを呼んだりしてしまう間違いが防げる。
web, 機能名
のような名前の2つのコンポーネントになる。
「悪魔は実装の詳細に宿る」として、なるべくスコープやアクセス修飾子で工夫し、コンパイル時に間違った実装方法は警告で分かるようにした方がよいとのこと。コンパイルがある言語が前提になっています。
他の分割方法としてはソースコードツリー、つまりリポジトリみたいなレベルでコードそのものを分割してしまう手法も紹介されています。
どんなにうまい設計でも実装方法が難しいと崩れてしまう、バランスよく考えねばならない……というまとめはその通りで、この章はアーキテクチャを検討する立場の人にはとても役立つ内容となっています。
自分が仕事で実際にやってきたプロジェクトでは、この分類では一番最初の、基本のレイヤー毎のパッケージ分けがほとんどでした。これで必要十分だったなあというところ。
開発標準を書いておいたり、簡単な機能一式のサンプルコードを手本に実装してもらう、コードレビューで発見……などの工夫で、実装方法の間違いは防げてきました。
第VII部 付録
付録A アーキテクチャ考古学
長いキャリアを誇るボブおじさまが過去にやってきた仕事を自叙伝的に振り返りながら、アーキテクチャ話や教訓を見ていくコーナー。
なんと45年の旅となっています。2020年の今からしたらエンジニア歴ほぼ50年、伝説の人ですねえ。他の本でもこの手の話はしたとのことで、1960~1990年代までを辿ります。写真も豊富で、博物館のごとくコンピュータの歴史を見ていくことができます。
昔すぎて技術が理解できない話もあるのですが、この方は泥臭い開発の現場で、本当に様々な苦難を乗り越えて経験を積んできたんだなーと思います。
その後の時代でサービス指向アーキテクチャやマイクロサービスアーキテクチャ、XMLなど明文化される技術と同じようなことを実務の中で編み出して活用している記述があり、なるほど実際の開発の歴史の中でこういう所に気付いて、職人の知恵、時代を超えた不変のルールに気付いたり編み出してきたのだなあと感心します。
- 画面とビジネスロジック、技術固有部分とビジネスロジックなど、境界線をきっちり分ける重要性
- プラグイン的に機能を追加した部分だけ、一部デプロイすれば済むような仕組みの重要性
- 前任者のコードが誰も弄れなくて結局全廃棄→他の人でも読んで理解できて修正できるクリーンなコードの重要性
- 別の考えの人がやった別の方式でもそれぞれ成功→アーキテクチャーに唯一の正解はない、銀の弾丸はない
- むかし流行りそうだったオブジェクト指向DBを導入したら使えずに大苦戦→DBはあくまで詳細、依存しないように切り離す
- 商用RDBを採用してCのコードのあちこちにSQLが散らばるまずい状況に、結局そのRDBが開発中止になって大苦戦→DBはあくまで(ry
などなど。これだけ苦労してくれば、「DBやフレームワークは詳細だ!(ドヤッ)」と主張強めに叫びたくもなるよなあとちょっと気持ちが分かります。
出てくる技術で言うと、後半にRational社に雇われてRational Roseの開発に携わった記述があります。
これはUMLがグラフィカルに設計できる、その昔有名だったツールです。ボブおぢさんの昔話が多いので習ってしまいますが、僕はこれをオブジェクト指向とUMLを学んでた頃に会社の部署で買ったのを使ってましたよ。(けっこうお高い製品でした)
2000年代前半、オブジェクト指向とサーバーサイドJavaの流れに乗って、駆け出しエンジニアから一気に成長しようとしていた頃でした。おおお……あのRoseにも関わってたのか……やっぱUncle Bobは凄い人や……!と尊みが増した気分になりました。
……技術的な展望は絶えず進化していくが、本書で紹介されているような基本原則はほとんど変わらない。
『リーンJSONクラウドNoSQL入門』なんて本があれば古本屋に売ってしまうかもしれないが、『Clean Architecture』はあなたの本棚に何年も置かれることだろう。本書があなたにとって設計の奥義となることを願っている。Bobの記事が私にとってそうであったように。本当の旅はここから始まる
と別の方が寄せた後書きに添えられている一節が実に良い。こうして設計とアーキテクチャの真理を巡る旅は終了するのでした。
まとめ:クリーンでより良いアーキテクチャ設計と、The Clean Architecture、(+昔話)が学べる本
エンジニア界隈では既にかなり話題になっている本ですが、学びと洞察がかなり広く得られる本でした。
- ボブおじさんの豊富な経験から導かれた、それぞれ個別に実践可能な、より良い設計とクリーンなアーキテクチャを追求する様々な原理原則、ヒント、助け
- その実践例としてボブおじさんの考えた最強のアーキテクチャ例として、
『The Clean Architecture』
と名付けられたアーキテクチャパターン
この2つの話が本書では語られていて、1.で登場する原則類はすべて本書独自という訳でもなく、他のソフトウェア開発の名著などでも時々出てきます。
そして2.の『The Clean Architecture』は、後半でやっと出てくるだけです。コード例も出てきますが抽象的なところもあり、現実ではなかなか適用しきれないところもあると思います。
ところが名付けがされていてタマネギ(あるいはドーナツ)の図が有名になったことから、2.の『The Clean Architecture』ばかりが注目されて誤解が生じたり、中にはちょっと違ってる実装例がネットに載ったり、一部でずれた広まり方をしたのかな……と思います。
ソフトウェア開発で時代を超えて通用する原則などを扱った本でたいてい語られているのは、すべての状況に対応できる銀の弾丸はなく、解決方法はひとつではないこと。本書でもバランスよくうまくやっていくしかないことは、(強い主張の陰でこっそりと)所々で述べられています。
ですのでこれから設計力をスキルアップしたい方は無理に完璧にやろうとせず、こういう役に立つ原則もいろいろあるんだよ、選択肢のひとつとして『The Clean Architecture』というアーキテクチャパターンもあるんだよ、こういうボブ老師によるクリーンアーキテクチャ流設計格闘術という流派(嘘)もあるんだよ、ぐらいの心持ちでいたほうが良いかと思います。
3文字略語の原則が色々出てきますが僕も略語の方はSRPぐらいしか覚えていないですし(笑)、これらの原則を振りかざして人のコードにケチばかりつけるような、原理主義(あるいは警察)的なアプローチになるのもエンジニアとしてアカンよなあとも思います。
実際、例えば1画面でDBも1テーブルでCRUDをやるだけのようなレベルの簡単なWebアプリケーションにこの『The Clean Architecture』を適用しても、学びになる以外は正直クラスの数が増えて大変になるだけだと思います。
頻繁に拡張したり技術要素が増えたり、機能規模が増えたり開発メンバーがどっと増えたり、自動テストがより重要になったりと育ったりしていく過程で、変更に強いアーキテクチャであることが実証される頃から真価を発揮するでしょう。
想像ですが、スピード最優先で作るより、今後も長く育って長くメンテナンスしていく見込みのサービスを本格的に作りこんでいくような開発に向いているのではと思います。
また、ドーナツ以前に本書で繰り返し語られているのは、
UI → ビジネスルール ← DB
の依存関係で、一番重要なビジネスの部分は他に依存せず独立させよう!という考え方です。この考え方は根本的には、ドメイン駆動設計(DDD)の考え方と似たところに行きつくんじゃないかなあと改めて思いました。実際、両者を結び付けて解説したりコード例を載せたりしている記事もあちこちで見つかります。
翻訳技術書ではおなじみ角征典さん、高木正弘さんによる翻訳は日本語は比較的読みやすく、ボブおじさんの力強いエモが伝わってきます。本書はひときわ主張が強めなのですが、「!」が効果的に使われています。
私的にはこの「!」の後に必要に応じて「(ドヤッ)」などを脳内補完して読むと、ボブおじさんのご尊顔が想像できて楽しく読めるかと思います。(おい)
プログラミングを始めてやっとアプリが動くようになったぐらいの方には、本書はまだハードルが高いでしょう。その先、ある程度の規模の開発を幾つかやって、あの時の設計のあそこがまずかった、ここは上手くやれたな……などと振り返りできるぐらいの方には、本書は大きく力になると思います。
よく勉強会のネタや輪読会の題材でも見かけますし、解説やコード例を載せている記事もネットにいろいろ見つかります。賛否両論ある本なのでそのへんを巡ってその人なりの解釈を読み取ったり、考えてみたりするのも面白いでしょう。あちこちにユーモアも散りばめてあり、楽しく読める本でした。
ボブおじさんのクリーン本シリーズなど
『アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技』はアジャイルを語る文脈でよく出てくるロングセラーですね。原著が2002年、翻訳が2008年、6000円ぐらいします。実は読めてません……
アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技
- 作者:ロバート・C・マーチン,Robert C. Martin
- 発売日: 2008/07/01
- メディア: 単行本
『Clean Code アジャイルソフトウェア達人の技』が原著2009年、翻訳が2017年。こちらはコードを書く際の諸々にフォーカスした本となっています。クリーンな気持ちになったのでこの本もその後読んでいます。ちなみにこちらはあまり叫んでいなくて穏やかです。翻訳もですます調になっています。
- 作者:Robert C.Martin
- 発売日: 2017/12/18
- メディア: 単行本
『Clean Coder プロフェッショナルプログラマへの道』が原著2011年、Kindle版が2018年に出ましたが紙の本は2012年からあり、帯の色が違います。プロフェッショナルなプログラマーとしての在り方、仕事への取組みの姿勢などにフォーカスした本。こちらもユーモアを交えてあり面白いです。
僕もだいぶ前に読んだあと会社の机の中に安置しているはずですが、どこに置いたかな……もうずっと物理出社してないな……
- 作者:Robert C. Martin
- 発売日: 2012/01/27
- メディア: 大型本
Clean Coder プロフェッショナルプログラマへの道 (アスキードワンゴ)
- 作者:Robert C.Martin,角 征典
- 発売日: 2018/08/01
- メディア: Kindle版
そして最新、原著2019年の『Clean Agile: Back to Basics.』が『Clean Agile 基本に立ち戻れ』として、今回も角征典さん、角谷信太郎さんの翻訳で2020年10月に登場予定です! 今度はチーム関連にフォーカスした話のようです。これは正座待機するしかない…
- 作者:Robert C.Martin
- 発売日: 2020/10/03
- メディア: 単行本
Clean Agile: Back to Basics (Robert C. Martin Series)
- 作者:Martin, Robert,Martin, Robert C.
- 発売日: 2019/10/17
- メディア: ペーパーバック