CleanArchitecture備忘録(前編)

Amazon CAPTCHA

いい本だと噂になっている気がしたので読んでみた備忘録。

日本語版もあるけど、英語の勉強も兼ねて英語版を読んでみた。

一旦PART 1 Introduction から PART3 Design Principle まで。

PART1 Introduction

What is Design and Architecture?

  • システムデザインの目的
    • システムを開発、メンテナンスしていく人的資源を最小化する
    • システムの質は顧客の要求に応えるために必要な努力量で計れる

システム開発はいろいろな手法やイケてる技術なマネージメント方法とかいろいろあってこのシンプルな目的を見失いがち。 手段の目的化も仕事を楽しく、豊かにしてくれるという意味で悪いことじゃないと思うけど、最終的にはこういった目的を共有できてないとカオスになりがち?

  • 早くやるための唯一の方法は、しっかりとやること
    • 自分のスキルに対する過剰な自信があり、速さ優先でガリガリ作っていくと、ウサギと亀の競争のうさぎののように、どこかで止まってしまったり、効率が悪くなってくるもの
    • シンプルなタスクでもちゃんとテストを書いたり設計を考えたり、着実にやったほうが結果的に早くなるという実験もある

要件の把握に対する自信過剰も結構ありそう。 どうしても手を動かして何かしらのアウトプットを出すことを優先する傾向がある気がする。(仕事をしてる感を出したい?)

アジャイル的な、手を動かしてプロジェクトを進めていくことで、メンバーのプロジェクトに対する知見が溜まっていくという意味で、早く手を動かすというのはいいと思うけど、 闇雲にプロトタイミングするより、どういう所が 要件的 or 技術的 に不安要素があるか?を落ち着いて整理してから手を動かす方がいい、というのもこのウサギと亀の話に通ずるところがありそう?

A Tale of Two Values

システムの振る舞いとアーキテクチャについて。

  • システムエンジニアはどちらの価値も意識して仕事をする責任がある
  • 振る舞いのほうを優先しがち
    • POからは振る舞いに関する話しかされない(当然、POが悪いというわけでは全くない)
    • アーキテクチャに対しての価値を高めるスキルを持っていないことが多い?

緊急性と重要性について

  • 緊急性のあるものは重要でない、また重要なものはずっと緊急にならない
  • 振る舞い は緊急的なもので、いつでも重要なものとは限らない
  • アーキテクチャ は重要なものだけど、けして緊急性は持たない
  • 緊急かつ重要 なものもあれば、 緊急ではなけど重要 , 緊急だけど重要ではない など色々ある
  • ちゃんと優先度を整理すればいいけど、 緊急かつ重要 なものより 緊急だけど重要ではない ものを優先してしまうことが起こりがち

アーキテクチャの話だけじゃなく、重要性についての優先度の整理は軽視されがちな気がする。 そもそも何が重要かを、コミュニケーションや組織体制的な問題 or 考えることがめんどうというあたりから、自然に判断するのが難しい状況がよくある?(かなり意識しないと自然と軽視してしまう状況)

やっぱり手を動かして仕事してる感を出したい欲が強いのもありそう。

PART 2 Starting with Bricks: Programming Paradigms

Structured Programming

  • プログラムの直接的な移動を制限する規律
  • 関数などで処理を構造化し、それらの挙動をテストすることで、より安全に大きな構造の構築を可能にする

goto文になじみがないので、Structured Programming 以前のことがイメージできない。 すごく大変そうだけど、こういった考え方をしらなければ大変さを自然に受け入れてる気がする。

Object-Oriented Programming

  • encaplulation
    • データとそれに関する関数を関連性の強いまとまりとして1箇所にまとめて、外部には必要なものだけを公開することで、外からは詳細な部分を見えなくする
    • それ OO言語と謳っていないC言語でできるよ。むしろ他のOO言語より完璧に。
  • inheritance
    • OO言語ができる前からC言語の構造体でおなじようなことを実現することは普通に行われていた
    • 若干トリッキーなやり方ではあるけど、OO言語が新しくもたらしたものと言うのは厳しい
  • Polymorphism
    • C言語でも同じようなことはできるけど、リスキーな形でしかできなかった
    • OO言語では自然に実装できる
    • OO言語は間接的なプログラムの移動を課す規律
    • 依存関係の反転
      • OO以前はflow architectureで、上位の関数が呼び出す必要がある下位の関数を知っている必要があった(下位の関数は上位のことは何もしらない)
      • Polymorphismを取り入れると、上位の関数が知っているのは、関数そのものではなくインターフェースを知っていればいい
      • 下位の関数は上位のインターフェースの定義に従って実装する
      • 依存関係をコントロールすることができる
      • アーキテクト目線からみた オブジェクト指向とは? の1つの答えは、依存関係を制御できるということ

良いシステムデザインとは変更が簡単にできること。 何にも依存していない、されていないコンポーネントはそれ自体がある程度複雑でも、コンポーネント内だけに集中すればいいのでだいたい変更が容易なことが多い。

逆にコンポーネントないとしては些細な変更でも、依存関係が強いコンポーネントはどこにどう影響するかが解りにくいということで、変更が難しくなる。

ということで、変更が簡単なシステムデザインを作る上での1つの大きなポイントは依存関係。

Polymorphismの抽象化による依存関係の制御はこの点でとても強いツールになりうるという感じ。

OOPとは?というような問はやっぱりよく解らないけど、 アーキテクト目線で とか視点を絞ってみるのは面白いなと。

Functional Programming

  • 関数型言語の変数は変化しない
  • race conditionsや deadlock が起きてしまうのは mutable な変数があるから
  • 無限のリソースがあれば完全な immutability をベースにしたシステムは現実的
  • 無限のリソースはないので何かしらの妥協を取り入れると現実的になる
  • イベントソーシングは全てのイベントを保持していて、状態を持たない、CRUDでいうと、CRだけがあるパターン

関数型的な考えはフロントエンド周りのfluxに触れたときに感じたものに該当する気がする。 stateを更新するときに、元のstateの更新をするのではなく、元のstateとactionから新しいstateを作る。 このときにstateはimmutableなオブジェクト。 stateを変更しないという制約があることで、race conditionなど考慮するところがなくなる。

まとめにある

What we have learned over the last half-century is what not to do.

Martin, Robert C.. Clean Architecture: A Craftsman's Guide to Software Structure and Design (Robert C. Martin Series) (p.56). Pearson Education. Kindle 版. 

の視点はなるほどなあという感じで面白い。

Design Principle

SRP

割とよく聞く名前でぼんやりと意識しているけど、 Responsibility 責務 とは? というのは深く考えたことはない気がする。 ケースバイケースじゃない?という感じで流しがち。

この本では、

1つのモジュールは1つのアクター、ただ1つのアクターの責任を持つ

という文で締めくくってる。

アクター 元々の文では、 reason to change となっていて、 reason to change というのはもっと具体的にいうと、システムはユーザー、ステークホルダーの要求の応えるためのものということで、 user or stakeholder という言葉に代替して、

最終的に、ユーザーとかステークホルダーというのは大体一人じゃなくてグループだよね、となり、グループを actor と表現している。

1つのモジュールが複数のアクターの要求時応じてしまっていると、異なる背景からの変更要求を1つのモジュール内でなんとか応えようと頑張ることになってまうので、複雑になってしまい、変更のコストが上がり、勿論バグも生まれやすい状況になってしまう。

設計時はオブジェクトのデータにフォーカスして考えがちだけど、ステークホルダー、アクターを意識して行うというのは面白そう。

OCP

システムは拡張に開いていて、変更には閉じているというもの。 本のサンプルは、財務レポートの出力形式の拡張に開いているというもの。

新しい出力形式を追加するとき

  • controllerは財務データを新しいプレゼンターに渡すだけ
  • 出力側の実装では、新しくPresenterを実装するだけ(既存のPresenterに変更は必要ない)

レポート用のデータを作成する とか レポートのデータを出力する といった役割がしっかりと分離できていれば自然と拡張が容易になりそう。 逆にレポートのデータを作成する処理に、htmlやpdfに関するものがまじってると厳しい、

どういった拡張を想定するか?が明確になっていれば設計、実装自体は割と簡単にできそう。

LSP

いわゆるインターフェースを定義したりするレベルでは自然とLSPに準拠するように作る気がするけど、アーキテクチャレベルの話だと、例のタクシー会社のように割と自然破ってしまう気がする。

それぞれのオブジェクトが持つ特異な部分は外部に漏らさず、内部で上手く処理することによってシステム全体絵の影響を抑えることができる。

ISP

Design Principleの中で一番ピンとこない。コンパイル必要な言語をあんまり使ってないから?

必要のないものに依存していると、それが予期せぬトラブルをまきおこす

まあ何か必要以上に大きなものに依存しているとき、本当に必要なもの絞ることでスッキリすることはありそう。 ただ構成要素が増えることによって複雑になることを避けたくなる。

大規模なチーム開発で依存関係をスッキリさせることの優先度が高いときとかはかなり有用な考え方になりそう?

DIP

  • 具体的なものではなく、抽象的にものにだけ依存しているコードのみから成るシステムが最も柔軟性がある
    • それは現実的ではない?
      • 挙動が変更される心配がない string のようなものには依存してもいい
  • 揮発性の高いものに依存しているものは不安定
  • 具体的なコードより、抽象化されたインターフェースのほうが変更されにくい
    • いいアーキテクチャはよりインターフェースの変更が起こりにくいようにするもの
    • 変更が起こりにくいインターフェースに依存しているコードは比較的安定する

Symfonyとかのサービスコンテナーいいよね。

Component Principle

最近のコンポーネント管理システムは便利だね。

コンポーネントをどういう基準で分割するかというのは難しい話で、プロジェクの性質や時期などによって良い基準というのは変わってくる。

同本で前述している Design Principle の SRP(Single Responsibility Principle) と ISP(Interface Segregation Principle) の考え方をコンポーネントの分割にも応用できる。

SRPはコンポーネントの開発者寄りの考え方で、システム全体から見た、そのコンポーネントの責任や、変更要因をベースに分割するので、コンポーネントのメンテナビリティが高めになりやすいといえる。

ISPコンポーネントの利用者寄りの考え方で、コンポーネントユースケースを想定してなるべく細かく分割することで、利用者がなるべく必要最低限のものにしか依存しないようにする。 必要なもの以外に多く依存してしまうと、コンポーネントのリリースを追いかけたり、変更内容を把握するのが難しくなり、さらに依存している小さな部分の変更によるシステム全体への影響にも気づきにくくなってしまうことが考えられる。

開発の初期段階では、より開発者寄りの考え(SRP)でメンテナビリティを高めて開発し、規模が大きくなり、色々なコンテキストを持つ利用者が増えてきたら、利用者よりの考えにシフトしていくのが良いと言っている。

感想

全体通読したけど、メインの部分と思われる PART5 Architecture よりそれ以前の話のほうが面白かった。 Architectureのところは数十人規模の大きめの開発向けな気がするから? なので後編を書くかは微妙。