No Purpose

If I must say, it's for me.

「クリーンアーキテクチャ」をこう読んだ

読み終えた。良い本だったと思う。

この本を読んだモチベーション

  • Goで書かれた簡素なレイヤードアーキテクチャによるマイクロサービスを会社で触る機会があったり、新たなサービスの切り出しを検討していたりして、Rails以外のサーバサイド設計についてもっと知見を深めたかった。
  • そんな中で、上司との1on1で「マイクロサービスパターン」を紹介してもらい、その本を読む前段の読み物としてよさそうと感じた。
  • Design It!」を社内の読書会で読んでいて、アーキテクチャ関連の本をほかにも読みたかった。

僕はこう読んだ

  • 例の"同心円状(玉ねぎ)のアーキテクチャ"(= "クリーンアーキテクチャ")の話以上に、その前提になっている「設計の原則」が重要に思えた。
    • SOLID(単一責任の原則 / オープンクローズドの原則 / リスコフの置換原則 / インターフェイス分離の原則 / 依存関係逆転の原則) を、通しで学べるのがよかった。
    • そのSOLIDの考え方を、コンポーネントレベルの議論に押し広げることで、より大きな粒度での設計を考える示唆が得られた
    • これらの考え方自体は"クリーンアーキテクチャ"を採用していなくても重要と感じる(たとえば、Railsでちょっと込み入ったことするときとか)
  • 同心円状の"クリーンアーキテクチャ"は、SOLIDを遵守するための1つのテクニック、という位置づけで捉えるのが自分にとってはちょうど良さそうに思えた。

「設計の原則」での学び: たとえば"DIP"における気付き

自分は"DIP"と"DI"を混同していたことに気がついた。

  • DIP: Dependency Inversion Principle」(依存関係逆転の原則)
  • 「DI: Dependency Injection」(依存性注入)

DIはたとえば、以下のようなコードを

class Foo
  def foo
    bar = Bar.new(params)
    bar.call
  end
end

このようにするようなテクニックと理解している。

class Foo
  def foo(something_like_bar)
    something_like_bar.call
  end
end

これによって、Fooクラスは、自分が依存しているBarクラスの存在や、その生成の方法を知らずに済む。 FooにBarという依存を注入した結果として、FooにBarFoo内でBarをnewするような"従来型の依存関係"に対する逆転をもたらす...つまり、"DI"は"DIP"のための手段だと理解できた。

その上で、この考え方をクラス間の依存ではなく、マイクロサービス間の依存に適用できないかと考えて、社のチャットにあれこれ書いていたら同僚の @qsona から有益なツッコミをもらった。 つまり、"DIP'が指す「従来型の依存関係の逆転」とは「"実体への依存"から"IFへの依存"への転換」という指摘だ。

上の例のように「Barクラスという実体」ではなく「 call メソッドを呼べるIF」への依存への転換が、依存性の逆転を実現する。 とすれば、マイクロサービスにおいても「サービス間で定めたIFを守っていさえすれば、対向サービスの実体を問わない」という考え方ができそうだ...。

と、気がついたところで、それってわりと普通のマイクロサービス間の関係だな、あぁこれがサービスを疎結合にするマイクロサービスのメリットなのかぁ、って当たり前のことに思い至ったりした。

同心円状の"クリーンアーキテクチャ"について

f:id:highwide:20200816004615j:plain
https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg

正直に言うと、これは本で語られるほど、いついかなるときも採用できるアーキテクチャではないのかなという印象を受けた。

この本では、繰り返し、DBやWebフレームワークといった"詳細"についての決定を遅延した経験やそこで得られるメリットが語られる。(ちなみに、"詳細"は"detail"の訳語で、"detail"には"些細"というニュアンスが含まれる旨が書かれていた)

ただそれは、現代のWeb開発においては必ずしも美談にならないのでは...という感想を抱いた。

たとえば、yasaichiさんの以下のスライドでは、Hanami(クリーンアーキテクチャを採用したRubyのWebフレームワーク)とRailsを比較しながら、密結合の功罪について明晰に語っている。

speakerdeck.com

あるいは、フロントエンドにおいても以下のエントリをきっかけにFirebaseを隠蔽すべきかという議論が盛り上がったことが記憶に新しい。

blog.ojisan.io

これらの議論は、現代のWeb開発においては"クリーンアーキテクチャ"が否定するような特定フレームワークやデータストアとの密結合を利用したいシーンがそれなりにあることを示唆している。 スタートアップにおいて素早くトライアンドエラーを行いたいときなど、意図して密結合を選んだうえで初速を得る選択肢も持つ必要があると思えた。

...何より、著者のアンクルボブ自身が以下のように語っている。

アーキテクチャ?御冗談を。これはスタートアップだぜ。アーキテクチャに時間をかけてる余裕はなかった。とにかくコードを書くんだ!コードがすべてだ!」

-- Robert C. Martin, 角征典, 高木正弘. Clean Architecture 達人に学ぶソフトウェアの構造と設計(第VII部「付録」-「明確なコミュニケーション」)

とはいえ言うまでもなく、"クリーンアーキテクチャ"を全否定したいわけではない。

特に、業務ドメインのシステム化が主眼となるエンタープライズの現場においては、ステークスホルダーの意思決定やが遅延したり、各種の政治的判断に振り回されがちな中で、"些細/詳細"な意思決定を遅延できるアーキテクチャというのは大いに価値がありそうに思える。

もちろん、Web事業開発においても、

  • トライアンドエラーのフェーズを経て、ユースケースやオペレーションが安定している
  • システム化したいドメインがある程度はっきり見えていて、かつ、それなりの規模になりそう。
  • 数年以上のスパンでの運用がもう見えている

といった状況にはフィットしやすいだろうなと思う。

この本との向き合い方

本著のまとめにあたる「第34章 書き残したこと」は、(なぜか)主著者のアンクルボブではなく、Simon Brown氏が筆を執っているのだが、アンクルボブがこれまで書いてきたことを大筋認めながら、ややバランスを取るような発言も目立つ。

使える選択肢は可能な限り残しておきたいが、理想に走りすぎてもいけない。チームの規模やメンバーのスキルやソリューションの複雑さ、そして時間と予算の制約などを考慮しよう。

-- Robert C. Martin, 角征典, 高木正弘. Clean Architecture 達人に学ぶソフトウェアの構造と設計(第VI部「詳細」- 第34章「書き残したこと」-「まとめ:言い残したこと」)

また、「訳者あとがき」でもこんな言葉があったりする。

ビジネスドメインに関係のないことは「詳細」と割り切り、デリバリーの仕組みやフレームワークなどをすべて後回しにしようとする。おそらく異論のある方もいらっしゃるだろう。私もすっきりしていない。

-- Robert C. Martin, 角征典, 高木正弘. Clean Architecture 達人に学ぶソフトウェアの構造と設計(「訳者あとがき」)

何度もアンクルボブが言い方と持ち出すエピソードを変えながら力強く主張してきたことに対して、同じ本の中で別の人がこんな発言をする懐の広さに驚いた。 そんな旨をツイートしていたら、t_wadaさんがレスをくれて、自分の感じていたこの本との向き合い方について見事に言い表してくれた。

...というわけで繰り返しにはなるが、僕はこの本をこう読んだ。

  • 結局、"クリーンアーキテクチャ" も適材適所で利用すべき
  • その採用とトレードオフになるものを頭に置きながら「"クリーンアーキテクチャ"の採用」自体も適切に遅延したい
  • 一方で、下敷きになっている原則には、クラス/コンポーネント/サービスという相似形において、適用しやすい有効なテクニックが多い