ぼくフルスタックエンジニア。

フルにスタックされすぎて積んでる人のブログ

クリーンアーキテクチャ本を読んだ感想

「クリーンアーキテクチャ 達人に学ぶソフトウェアの構造と設計」を読んだのでその感想と次やることを書いていきます。(感想というより読書メモだなこれ)

動機

最近、設計をちゃんとしたアプリを開発したいと思い、いくつかクリーンアーキテクチャをベースとしてコードを書いているつもりです。まずはコードを写経して、クリーンアーキテクチャがどんなものか知りました。カラオケのバイナリ解析アプリを作るなどして実際の個人開発で使ってみました。また、今やっているGo言語のプロジェクトでは、クリーンアーキテクチャをベースにボイラープレートを作ることでチームで担当分けした開発がしやすくなりました。

github.com

しかし、書いているうちにしばしば、本当にクリーンアーキテクチャとして正しいのか不安になることがありました。例えばGo言語のプロジェクトでは、まだ開発中ですが、つなぐ部分のコードばかり大きくなり実際の実装は数行になっていたりします。カラオケのものでも、結局カラオケの歌詞バイナリデータから汎用的なデータに変換するために、コアのロジック(ドメイン)でバイナリを扱っていたりして、本当にいいのかと不安を持つこともありました。

この点を踏まえ、今回はこのクリーンアーキテクチャの本、特に後半のアーキテクチャの話について詳しく読んでいきました。

メモ

コンポーネントの依存グラフにおける循環依存があるとコンポーネントが密結合して面倒になる

正直、これまでの設計でやってしまっていると思いました。これはクリーンアーキテクチャの丸い円だけを見ていても気づけない問題です。

この図でEntitiesからAuthorizerに伸びている依存は、これのせいでAuthorizerとInteractorsとEntitiesが有効エッジで循環させている。これがあると、DatabaseがこれまでEntitiesとの依存性だけを考えていたのに、Authorizerへの依存性や、それ経由でのInteractorsまでへの依存性まで考えないといけなくなるらしい。

これはただ、DIPで依存性を逆転するだけで解決する問題ですが、インターフェイスをバンバン作っていたらそれはそれで循環依存になってしまいそうで怖いです。クリーンアーキテクチャを書き始めてから全部継承にして、極力依存させないぞなんてやっていたのでコンポーネント図も書いて意識したほうがよさそうです。

f:id:gpioblink:20200417213029p:plain

プロジェクトの開始時にコンポーネント図を作ることはない(できない)

私は、今まで設計は最初にやってそれをもとに組み立てて行くのを続けていました。何か新しいものを開発することになれば、とりあえず紙を持ってきてマルをグルグル書いて、クリーンアーキテクチャを組み立てていました。あとはこれを元に円の真ん中から書いていくようなことをしていました。

さっきの密結合の話でよし、じゃあ最初の設計段階でグルグルの他に書いてみるかと思った矢先、それは無理らしいです。MainとかViewとかPresenterが並んでいる典型的な図を見る限り初期でもある程度はできそうだと思っていたのですが、これは作ってみてから依存性を把握するための地図という位置づけのようです。

f:id:gpioblink:20200304115159p:plain

この本では、コンポーネント図はアプリケーションの機能について書くわけではなく、ビルド可能性や保守性を見るための地図だと書いてあります。とにかく、相互の関係をどう落としていくかの話はレイヤーが違うと気付かされました。

安定度・抽象度等価の原則(SAP)と安定依存の原則(SDP)の組み合わせが、コンポーネント版の依存関係逆転の原則(DIP)に相当する

あまり意識していませんでしたが、クリーンアーキテクチャは型のない言語にも使えるようです。

抽象度は「I = コンポーネント内の抽象クラスとインターフェイスの総数 ÷ コンポーネント内のクラスの総数」、 安定度は「ファン・アウト(依存入力数) ÷ (ファン・イン(依存出力数) + ファン・アウト)」で出すらしいことを知りました。 そして抽象度が高くなる方向へ依存するようです。

本でも「コンポーネントの安定度や抽象度はきちんと区別できるものではない」と言っている通り、正直これを知ってしても型なしの言語で上手く活用出来る気はしませんが、近いうちにPythonでなんか書いて使ってみたいと思います。苦痛ゾーンとか無駄ゾーンに分けてコードをちょっとだけ数学的に評価するのとかやってみたい。

ユースケースとはこんなもの

こういうのって様々な意見があるんだろうけど、これは実際のコードを見る中でもかなり上手く言語化されているように感じてとても好きです! 人によってはユースケースに「〇〇というボタンを押したら〇〇が起きて…」のような詳細が書かれていることがありますが、なんか粒度が細かすぎる気がして本当にそれでいいのかと疑問になることがしばしばあります。

これはユースケースなので、ここから呼ばれるものはビジネスロジックです。つまり円の中央ドメインに何を置くべきか書いてあるとも言え、この例はとても貴重な気がしています。

f:id:gpioblink:20200416164354p:plain

アーキテクトの目的は、方針とは無関係に詳細を決めながら、方針をシステムの最も重要な要素と認識するシステムの形状を作ること

あまり長期的な目線で設計していなかったので、この考え方は斬新でした。確かに、開発初期でWebサーバで配信するかREST使うかは決める必要はありません。 詳細の決定を延期や保留することができるのは、確かに仕様が確定していない開発段階などから使えて重要そうです。

組み込みクリーンアーキテクチャでは、OS抽象化レイヤー(OASL)やハードウェア抽象化レイヤー(HAL)を使って詳細をユーザーに明らかにしなくする

ロボットサークルとかでmbedのコードを書いたりしますが、どうしてもESP(WiFi)を使うとき送信時のウェイトなどでハード依存になっている所がありそうです。ある程度は関数などを活用して分けられていると思いますが、抽象化レイヤーという意識をすればもっと分離できそうです。同じ意味で、条件付きコンパイルとか多用するのも「Don't Repeat Yourself原則」違反にもなり好ましくないらしい。

新機能をポリモーフィックに扱うようにしないと、機能追加などで全てのサービスを変更するハメになる

新機能の追加をオープン・クローズドの原則にしたがってできるようにするには、ロジックを依存性の関係に従って抽出できるようにする必要があるようです。 この辺りは経験を積まないとどんな感じで分けられるか分からないような気もしますが、将来の変更も意識できるようになると強そうです。

f:id:gpioblink:20200417221612p:plain

感想

今回、この本を読むことで「なんとなくクリーンアーキテクチャっぽく円を書いて設計していた」部分で、依存性循環とか具体性があいまいなユースケースなど実はヤバそうだと思う例を見つけることが出来ました。元から、コンポーネントでやる部分を明確にできるので、あまりコミュニケーションが取れない環境でチーム開発する際とかにも重宝しそうな設計だと思っていました。この結果を元にさらにいくつかアプリを作って、とりあえず個人レベルでまずはより変更しやすいクリーンアーキテクチャを目指して設計してみようと思います。

次やること

この本を読んでも、やはりアイディアからコンポーネントに落とすまでのフローが部分的には情報があるものの具体的に書いてあるわけではなく、もう少し設計を学んでいきたいと思っています。今後は、とりあえず聞いたことのあるDDDやTDDといった手法について詳しく学んで、設計についての知識を深めていきたいと思っています。