kbigwheelのプログラミング・ソフトウェア技術系ブログ

プログラミング・ソフトウェア技術系のことを書きます

clean architecture 読んで心に残った箇所の覚書

Clean Architecture 達人に学ぶソフトウェアの構造と設計【委託】 - 達人出版会

アーキテクチャは昔から作り方がずっと一緒、おれがその最終解を教えてやる

眉が唾液でベトベトになる

第3章 パラダイムの概要

if for whileは50年変わっておらず、プログラミングの主要なパラダイムは関数型、構造化、オブジェクト指向の3つで、これらは全て1958〜1968にみつかった。だから最終解がある。

なるほど、それを聞くと一定の説得力がある気がする。

第4章 構造化プログラミング

だいたい同意できる

第5章 オブジェクト指向プログラミング

cを引き合いに出してooのカプセル化をディスっているところ、論理展開がよくわからない。訳が微妙なせい?

やたらcを引き合いに出す。さすがに偏見を感じる。

第6章 関数型プログラミング

エベントソーシングとの関連付けは見事。

繰り返し述べられる、プログラミングパラダイムは進化していないと言う主張を今では完全に信じている。

第III部 設計の原則

バズワード的に捉えていたsolid原則がすんなり頭に入る。

第7章 単一責任の原則

前半の説明は違和感なく納得できるが後半の説明がわからない。

関数を別のクラスへ分離するのはともかく、employeesataを共有するということはデータ構造、あるいはデータそのものも共有している?それだと意味なくない?

   

ここから風呂で本を読んでメモを怠る

どこだかで書かれていた、最終的にはトップレベルではDIを注入しないといけないという記述は自分がやっていることが正しいと裏付けできて非常に心強くなった。

第IV部 コンポーネントの原則

第14章 コンポーネントの結合

ここで出てくるメトリクスを昔のように乱用しないこと。

あくまで重要なのはここでの考え方が重要で、メトリクスはその目安にすぎない。 メトリクス指標を追いすぎると本来の考え方が軽視されてしまう(手段と目的の逆転)。

第V部 アーキテクチャ

第15章 アーキテクチャとは?

話がかなり抽象的になったためかまた一気にわかりづらくなる。

言いたいことはなんとなくわかるが古い例え話が多いこともありすっと頭に入ってこない。今後の章を読んで理解が深まることを期待して一旦理解を保留。

第16章 独立性

気をつけてほしい。反射的に重複を排除する罪を犯してはいけない。その重複が本物かどうかを見極めるべきだ。

DRY偏重な考え方に対するカウンター。確かに僕もこれで痛い目を見たことが何度か。

私の好みは、いざというときのために、サービスを作れそうなところまで切り離すというものである。

なるほど、だいぶわかってきた。

私が言っているのは、システムの切り離し方式は時間とともに変化する可能性があるということだ。

第17章 バウンダリー:境界線を引く

著者が開発していたFitNisseについて、最後までストレージの意思決定を遅らせた結果、当初予定していたMySQLは最終的に不要とわかりファイルシステムへ書き出すだけでOKになった。

 > これも単一責任の原則(SRP)である。単一責任の原則(SRP)はどこに境界線を引けばいいかを教えてくれる。

これは、依存関係逆転の原則(DIP)と安定度・抽象度等価の原則(SAP)を適用したものであると認識するべきだ。依存性の矢印が詳細レベルから抽象レベルを指すようになっている。

第18章 境界の解剖学

モノリスからパッケージレベルの分離、そしてもっとも大きな粒度の分離である(マイクロ)サービスレベルの分離など。

第19章 方針とレベル

度々引き合いに出されるSOLID則の汎用性にはちょっとビビる。 これを覚えておけば、すべての間違ったアーキテクチャへの反論として使えるのでは?

いや、でもそれ相手の知らない言葉で相手を押さえつける一番僕が嫌っていた手法だったわ。 相手を納得させたいなら、あくまで相手の言葉と相手の問題意識に沿った方法でやらんといかん。

第20章 ビジネスルール

では、なぜエンティティが上位レベルで、ユースケースが下位レベルなのだろうか? ユースケースはアプリケーション固有なので、システムの入力と出力に近い。エンティティは複数のアプリケーションで使用できるように一般化されているので、システムの入力と出力から遠く離れている。したがって、ユースケースはエンティティに依存し、エンティティはユースケースに依存していないのである。

clean architectureとlayerd architectureのパット見てわかる差別点である、ドメイン層とエンティティ層/ユースケース層の区別。 clean architectureではビジネスロジックを実装するための層としてエンティティ層とユースケース層を分けている。 その背後には上のような、両者のレベルの違いという考えがあるようだ。

第21章 叫ぶアーキテクチャ

うーん、書いてあることは理解できるがまとめるに困る章。 システム設計者がエンティティ・ユースケースへちゃんとビジネスロジックを集約していることと、そのコードを読む人がclean architectureを理解している前提がないと、自明なアーキテクチャという理解の合意は難しいと思うんだがね。 まあ提供方法次第か。

第22章 クリーンアーキテクチャ

ヘキサゴナルアーキテクチャ、DCIアーキテクチャ、BCE(そしてたぶんレイヤー化アーキテクチャも)、いずれも「関心事の分離」という同じ目的を持っている。中略。また、それぞれ少なくとも、ビジネスルールのレイヤーと、ユーザーやシステムとのインターフェイスとなるレイヤーを持っている。

例の有名なクリーンアーキテクチャの図、コントローラとプレゼンターがクラシカルなウェブアプリの入力(HTTPリクエスト)と出力(HTTPレスポンス、それもHTMLをイメージした)であることがわかったことが収穫。

第23章 プレゼンターとHumble Object

まずは 、前提を明らかにしよう 。 O R M (オブジェクトリレ ーショナルマッパ ー )というものは存在しない 。

相変わらず主語が大きいことを言い切るスタイル。

O R Mシステムはどこに属するのだろうか ?もちろんデ ータベ ースのレイヤ ーだ 。

あじか、僕が思っていたより相当多層的だな

第24章 部分的な境界

YAGNIの法則には反するが、著者は予防的な構造化を行う。 ただし、明確に境界を定めないとやがて境界線が曖昧になり依存性が双方向など違反してしまいがち。

第25章 レイヤーと境界

GameRulesの内部を見ると、GameRulesに含まれるコードが使用して、Languageのコードが実装しているポリモーフィックなBoundaryインターフェイスが見つかる。同様に、Languageに含まれるコードが使用して、GameRulesのコードが実装しているポリモーフィックなBoundaryインターフェイスも見つかる。

長いこと読んで初めて違和感のある訳に遭遇。 これは原文に忠実に訳しようとした結果日本語へマッピングしづらかった例なのでまだ解読しやすい。 語順を無視して日本語として読みやすい形にすると以下ぐらいか。

GameRulesの内部を見ると、Languageのコードで実装されているポリモーフィックなBoundaryインターフェイスがGameRulesに含まれるコードで使用されているのが見つかる。同様に、GameRulesのコードで実装されているポリモーフィックなBoundaryインターフェイスがLanguageに含まれるコードで使用されていることも見つかる。

ここまでにやってきたことは何を意味するのだろうか? 200行のKornshellで実装できるシンプルなプログラムに、どうしてわざわざアーキテクチャの境界を作ったのだろうか? この例は、アーキテクチャの境界があらゆるところに存在することを示している。我々アーキテクトは、それがいつ必要になるかに気を配らなければいけない。また、境界を完全に構築しようとすると、コストが高くつくことを認識する必要がある。

ふんふんとそれらしいなと読んでいて章の最後でちゃぶ台返し。 そうよね、やっぱり境界区切ればよいってもんじゃないよね。

しかもこれは、1回限りの決定ではない。プロジェクトの開始時に、実装する境界と無視する境界を決めればいいわけではない。常に見張る必要がある。

アーキテクチャは常に進化・変化すること。またアーキテクチャが常にリードエンジニアでもあるべき理由。

第26章 メインコンポーネント

すべてのコンポーネント/レイヤーの最も外側、もっとも下位レイヤーであるmain関数の話。 (DIはここで行われる。これ重要。)

clean architectureの図だけ見ているとこのDIや最も汚い組み立てをどうするのかがわからなくなりがち。 というのもこの章で語られていることはかなり控えめにしかかかれないから(この章も最終盤だし)。 個人的にはclean architectureの最も外側の層として図示してもいいレベルで重要だと思うんだけど(初学者がわからなくなりがちな点も考慮して)。

Mainをアプリケーションのプラグインと考えよう。初期状態や構成を設定して、外部リソースを集め、アプリケーションの上位レベルの方針に制御を渡すプラグインである。プラグインなので、アプリケーションの設定ごとに複数のMainコンポーネントを持つこともできる。 たとえば、開発用、テスト用、本番用のMainを用意することもできる。あるいは、デプロイする国別、権限別、顧客別に用意することもできるだろう。 Mainをアーキテクチャの境界の背後にあるプラグインとして考えると、設定の問題はもっと解決しやすくなるはずだ。

なるほど、この考え方はテスタビリティを改善するかも。

第27章 サービス:あらゆる存在

●サービスが互いに分離されているように見えるから。あとで説明するが、これは部分的にしか正しくない。 ●サービスが開発とデプロイを独立させているように見えるから。これもあとで説明するが、部分的にしか正しくない。

初手ビーンボールはMartinの変わらぬスタイル。

これは「横断的関心事」の問題である。あらゆるソフトウェアシステムは、サービス指向であろうとなかろうと、この問題に直面することになる。図27-1のサービス図のような機能分割は、すべての動作に影響を与える新機能の追加に対して非常に弱い。

なるほど、機能ベースでのマイクロサービスへの分割はこのような結果になるのか。

これしかしKitten配達サービス始める前にこのような抽象化を始めるのはYAGNIの法則に強く違反していないかなあ。それこそ抽象化の方法はいくらでもあるように思える(1つのりんごを2つに切る方法が無数にあるように、抽象化の方法も無数にあるのでは?)

横断的関心事

ココらへんがちょっとよくわからない。 横断的関心事についてあとで調べたほうが良いかも。

第28章 テスト境界

こうした状況は深刻化する可能性がある。共通のシステムコンポーネントを変更すると、何百や何千というテストが壊れる可能性がある。これは、 脆弱なテストの問題(Fragile Tests Problem) と呼ばれている。

確かに。

テストAPIの目的は、テストをアプリケーションから分離することである。この分離は、UIからテストを切り離すことだけではない。アプリケーションの 構造 からテストの 構造 を切り離すことが目的である。

テストAPI

具体的なイメージがわかない。 関数/メソッドレベルでの結合を避ける必要がある? わかりづらいのでここは後で読み直そう。

第29章 クリーン組込みアーキテクチャ

この章長いが、ちょっと読むと本当に組込ソフトウェアの話がメインっぽいので流し読みでも良いかも。

Kent Beckが、ソフトウェアを構築する3つの活動について、以下のように説明している(強調は著者による)。 1. まずは、動作させる。動作しなければ、仕事にならない。 2. それから、正しくする。あなたやほかの人たちが理解できるようにコードをリファクタリングして、ニーズの変化や理解の向上のためにコードを進化させていく。 3. それから、高速化する。「必要とされる」パフォーマンスのためにコードをリファクタリングする。

基本にして奥義、みたいな話やな。

ソフトウェアとファームウェアを混ぜるのはアンチパターンである。

この章で言いたいことの半分くらいはコレの気がする。

組込みアプリケーションが特殊なツールチェーンを使用している場合、「便利な」ヘッダーファイルが提供されていることがある

唐突に挿入されるインデントタグ。多分コピペで入っちゃって校正すり抜けちゃったっぽい。

第VI部 詳細

(訳注:「詳細」を意味する「detail」には、「些細」という意味もある。本書では「詳細」で統一しているが、両方の意味が含まれることに注意してほしい。)

いいっすねー。こういった手を抜かない翻訳姿勢。 こういった注釈を書く、書ける、書く価値がある、書くテクニックがあることは自分が翻訳文章書くときのために覚えておこう。

第30章 データベースは詳細

過去スタートアップでRDBMSの不要さを説いて、結果的に追い出された話、切ない。 そういうもんよな。

第31章 ウェブは詳細

計算をサーバサイドで主にするかクライアントサイドで主にするかの揺れ・揺り戻しを振り子と表現するのは言い得て妙。

ただ、WebサイドのアプリケーションでGUIビジネスロジックの分離が難しいと著者が認めているのは2018年時点では現実的か。 それでもreactなどの解答はできつつある気はする。

第32章 フレームワークは詳細

フレームワークの作者に対して辛辣すぎじゃない?w rails系の人への批判なんだろうか。少なくともsinatraはそんな思想ではないと思うけど。

まあたしかにplayやakka-httpのテストフレームワークはダサすぎてあれ一緒に使うの矯正されるのは嫌だけど。

フレームワークをコアのコードに混ぜないこと(プラグインすること)

これは現在のプロジェクトでakka-httpを別レイヤーとして分離すること、できたことで多少なりともメリットを実感できた(多くのboundaryクラスを書くことと引き換えだったけど)。 どちらかというと長期的なメンテナンスで徐々にこのメリットを感じていくと思う。

第33章 事例:動画販売サイト

意外と短くて例としてはあまり役に立たなかった。 これ後で書こうとも思っていたけど具体例がかなり少ないのが辛い。 実践ドメイン駆動設計を読んでいる途中のためもあるけど、もっと現実に近い例がみたいね。

第34章 書き残したこと

今のチームでそろそろ導入しようと思っていた、コンパイル前のimportのルール違反チェック機構がここで紹介されているのを見て、ああ、他にも同様に考えている人がいたという安心。

そうよね、そしてやはりコンポーネントによるパッケージングはいわゆるsbtでいうモジュール、javaでいうjar単位の分割か。

 「コンポーネントによるパッケージング」の大きな利点は、もし注文に関するコードを書きたいのなら、OrdersComponentだけを見ればいいという点だ。

これなんよなあ。大抵のカウボーイ型のコーダーは本質的に自分しか見ることを考えないから、こう、変更必要な箇所が絶対的に特定かつ限定されるというオープンクローズドの原則の力を過小評価しがちなんよな。

あと読んでいて機能ベースのパッケージングを早急にしないとと思った。 あれ縦割り横割りって選択肢じゃなくて両方やるもんなのな。 機能単位でパッケージングしたのち、レイヤーの違いはコンポーネントで表現すると。