k41531

クリーンアーキテクチャ

本記事は、Clean Archtecuterという書籍の「第22章 クリーンアーキテクチャ」「第23章 プレゼンターとHumble Object」「第24章 部分的な境界」を読んで自分なりにまとめたものです。プレゼンの台本のような形で記録が残っていたのでブログ記事として供養させていただきます。


過去のアーキテクチャ

  • ヘキサゴナルアーキテクチャ
  • DCIアーキテクチャ
  • BCE

いずれも「関心ごとの分離」という同じ目的を有している。 ソフトウェアをレイヤーごとに分離することでその目的を達成している。 最低限のレイヤー

  • ビジネスルールのレイヤー
  • ユーザーとシステム間のインターフェースとなるレイヤー

これらのアーキテクチャは以下の特性を持つシステムを生む。

  • フレームワーク非依存:機能満載なライブラリに依存しない(そのライブラリに縛られた設計をしない)
  • テスト可能:ビジネスルールは外部要素(UI、データベース、ウェブサーバ、そのほか外部要素)がなくてもテストができる。
  • UI非依存:UIを他の部分に影響を与えず変更できる(GUIからCUIへの変更など)
  • データベース非依存:データベースを他の部分に影響を与えず変更できる(SQLからNoSQLへの変更など)
  • 外部エージェント非依存:ビジネスルールは、外の世界のインターフェイス(DB,UI,Webなど)について何も知らない

あの図はこれらのすべてのアーキテクチャを単一実行可能なアイデアに統合したもので、それがクリーンアーキテクチャ。

クリーンアーキテクチャという概念をどう捉えるかについては、大きく分けて二つの意見があります。 一つは、前述の図に登場する用語や概念を用いて、4層レイヤーから成るAPアーキテクチャを指す場合です。ほとんどのシチュエーションではこちらの意味で使われます。 一方で、クリーンアーキテクチャで重要とされる「依存性のルール」、「関心の分離」、「依存関係逆転の法則(DIP)」などに着目し、図には拘らない方が良いという意見もあります。実際に提案者は、図は概要であり、クリーンアーキテクチャは4層以上にもなりうると述べています。

図の円は概要を示したものである。したがって、この4つ以外は認めないというルールはない。

-- Robert C.Martin,角 征典,高木 正弘. Clean Architecture 達人に学ぶソフトウェアの構造と設計 (Japanese Edition) (Kindle の位置No.3161-3165). Kindle 版.

Tips: 外側が次の内側からしかさわれないというルールはない、したがって、ひとつレイヤーを飛ばした内側に依存することも当然できる。

依存性のルール

  • 円の中央に近づくほどレベルが上がっていく
  • 円の外側は仕組み、内側は方針
  • 最も重要なルールは、依存性のルール

ソースコードの依存性は、内側(上位レベルの方針)だけに向かっていなければいけない

  • 円の内側は外側について何も知らない
  • 外側で宣言された名前に、内側で触れてはいけない
    • 関数
    • クラス
    • 変数
    • その他名前付きのソフトウェアエンティティ
  • データフォーマットも同様

エンティティ(レイヤー)

企業全体の最重要ビジネスルールをカプセル化したもの。 エンティティ自体はアプリケーションから使用できるならなんでも良い。(メソッドを持つオブジェクトでもデータ構造と関数でも)

外部からの影響を全く受けない

ユースケース(レイヤー)

アプリケーション固有のビジネスルールが含まれている。
システムの全てのユースケースがカプセル化・実装されている

  • エンティティに入出力するデータの流れを調整する
  • ユースケースの目的を達成できるように、エンティティに指示を出す

外部からの影響を受けないし、エンティティにも影響を与えない

ただ、アプリケーションの操作の変更された場合、ユースケースレイヤーのソフトウェアに影響を与えることはある。

アプリケーションの操作の変更 -> ユースケースに影響 -> ユースケースレイヤーのソフトウェアに影響

もちろん、ユースケースの詳細が変更されたら、ユースケースレイヤーのコードも一部影響を受ける。

インターフェイスアダプター

ユースケースレイヤーやエンティティレイヤーに便利なフォーマットから、外部エージェントに便利なフォーマットにデータを変換するアダプター
※ここでのフォーマットとは、粒度の小さいデータのこと? (jsonの中身の形の変換)

GUIのMVCアーキテクチャを保持するのはこのレイヤー

  • モデル:ただのデータ構造
  • コントロール:ユースケースとのインターフェイス
  • ビュー:UIとのインターフェイス

ユースケースレイヤーやエンティティレイヤーに便利な形式(form)から、DBに便利な形式(form)にデータを変換するアダプター
※ここでの形式とは、粒度の大きいデータのこと? (jsonからCSVへの変換)
データベースがSQLならば、すべてのSQLはこのレイヤーに含める。

外部サービスなどの外部の形式から、ユースケースやエンティティが使用する内部の形式にデータを変換するアダプターも含まれる。

要するにデータの変換は全てここで行う。

フレームワークとドライバ

フレームワークとツールで構成されている。 例えば、ウェブフレームワークやデータベース。 このレイヤーにはコードをあまり書かない 書く場合は一つ内側とやりとりするグルーコードくらい。

このレイヤーには詳細が詰まっている。ウェブも詳細、データベースも詳細。

4つの円だけ?

4つ以外認めないというルールはないが、依存性は常に内側を向けるべきであり、内側ほど抽象度と方針のレベルは高まる。

境界線を越える

境界線を越えることがあっても、内側が外側に依存するようなことがあってはならない

コントローラを操作して、ユースケースレイヤーを通り、プレゼンターに出力する際 ユースケースはプレゼンターを呼び出したいが、直接呼び出しはできない。 ユースケース内のインターフェイスを呼び出すようにし、円の外側にあるプレゼンターがインターフェイスを実装する

考察:インターフェイスとはこういこうことをしたいと書かれたルールみたいなものなので、ユースケースに含まれる。そして、実装を外側でしてあげれば依存することはない。

境界線を越えるデータ

独立した単純なデータ構造であることが重要 そのレイヤーで使われるデータのまま渡すのではなく、渡されるれるレイヤーが使いやすいデータの形式で渡す。

参考文献

https://gist.github.com/mpppk/609d592f25cab9312654b39f1b357c60 https://blog.tai2.net/the_clean_architecture.html https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

プレゼンターとHumble Object

テストしにくい振る舞いとテストしやすい振る舞いを分離するために生み出されたデザインパターン

  • 振る舞いを2つのモジュールまたはクラスに分割するだけ!
  • 一つのモジュールが「Humble(控えめ)」<- テストが難しい
  • もう一つがテストがしやすいモジュール

プレゼンターとビュー

GUIはユニットテストが難しい、画面に適切な要素が表示されているかの確認が困難 よって、GUIをHumble Object によって Presenter と View の2つのクラスに分ける。

Viewがすることは表示だけ。 Presenterで表示の前にすることは全部済ませて、ViewModelを作成し、配置する。 Viewは配置されたView Modelを表示するだけ。

参考文献

https://www.slideshare.net/hirotoimoto1/humble-object-pattern

データベースとゲートウェイ

データベースゲートウェイはユースケースインタラクターとデータベースの間にあるもの。 データベースに対する、作成、読み込み、更新、削除のメソッドを含んだポリモーフィックインターフェイス。 ユースケースレイヤーではSQLの使用が許されない代わりに、ゲートウェイにメソッドを用意する。 ゲートウェイ自体の実装はDBレイヤーで行う <ー これがHumble Object メソッドから要求されたデータにアクセスするためにSQLが書かれているだけ。

参考文献

エンタープライズアプリケーションアーキテクチャパターン https://shtnkgm.com/blog/2020-05-17-humble-object-pattern.html

ORM

Wikipediaより抜粋。

オブジェクト関係マッピング(英: Object-relational mapping、O/RM、ORM)とは、 データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法である。 オブジェクト関連マッピングとも呼ぶ。実際には、オブジェクト指向言語から使える「仮想」オブジェクトデータベースを構築する手法である。 オブジェクト関係マッピングを行うソフトウェアパッケージは商用のものもフリーなものもあるが、場合によっては独自に開発することもある。

ORMは存在しない(言葉遊び)、Objectはデータ構造ではなく操作の集合。 データ構造は、振る舞いを持たないデータの変数の集合だるので、ORMは「データマッパー」と呼ぶのが適切。 RDBから読み取ったデータをデータ構造に詰め込むから。 データベースのレイヤーに配置される。

まとめ

  • アーキテクチャの境界の近くには Humble Object パターンが潜んでいる。
  • 境界を越える通信はシンプルなデータ構造。
  • 境界はテストしにくい部分としやすい部分を分割する
  • テストの容易性が向上する

部分的な境界

  1. 境界のコストは高い
    1. Boundary interface
    2. I/O のデータ構造
    3. 両サイドのコンパイルやデプロイを独立させるための依存性管理
  2. コストが高いが、今後必要になるから境界を残したい
  3. YAGNI原則の違反になるが、必要となることだってある。
  4. 部分的な境界を作ることになる

最後のステップを省略する

両サイドのコンポーネントを独立して作った上で、一つのコンポーネントにまとめる。

メリット

  • 複数のコンポーネントを管理する必要がなくなる
  • バージョン番号の追跡の負担がない
  • リリース管理の負担もない

デメリット

  • 二つのコンポーネントを合わせているので放っておくと分離が弱くなる
  • 分離しにくくなる

(分離が)片方だけの境界

依存性の方向は内側に向いた上で、両サイドは独立でコンパイルされないといけないことに注意。

境界と依存の区別をちゃんとしなければならない 依存は振る舞いに影響を受けるかどうかの話であり、ソースコードの修正を強いられるかの話ではない。境界線を正しく引くことができればどちら側を変更しても片方が変更を強いられたりはしない。

https://qiita.com/YokoKen/items/9cb810c3032e1011a189