じぶん対策

日々学んだことをアウトプットして備忘録にしています。

web初学者によるクリーンアーキテクチャの理解

クリーンアーキテクチャについて

f:id:taisei_miyaji:20220321081706p:plain

参考

実装クリーンアーキテクチャ
クリーンアーキテクチャ完全に理解した
元ネタのブログ

クリーンアーキテクチャって?

まずGoogle先生に「クリーンアーキテクチャ」って何か聞くと上記の画像がヒットします。
見たことある人も多いのではないでしょうか。元ネタのボブおじさんのブログのリンクも貼ってあるので参考にしてください。
具体的なコードを見る前の段階として日本語や図で設計を理解することを目的としています。
「クリーンアーキテクチャって何ですか?」って聞かれた時にうまく言語化することが目標です。
(具体的な実装については参考に挙げたサイトを見ることをお勧めします。)
今回の目標はこの図を理解することです。

本質となる考え方

  • ビジネスロジックを独立させる。そのためには以下の二つを守る。
  • 独立させるメリットは取り換え可能になる = テストがしやすい。
  • 要するにメンテしやすくなる。

Enterprise Business Rules(Entities)

図の黄色のレイヤーです。
この層はビジネスロジックを表現するオブジェクトが所属している層です。
DDD(ドメイン駆動設計)が最も影響する層。 ビジネスルールをカプセル化したもの。

Application Business Rules(Use Cases)

図の赤色のレイヤーです。
「ソフトウェアが何ができるのか」を表現します。
アプリケーションの目的であるドメインにおける問題を解決するためにドメインオブジェクトを束ね上げ、ユースケースを実現する。
ちょっと難しいので例を出してみます。
例えば、買い物するとき、「注文」「受注」というドメインオブジェクトがあります。
ここでシステムを作る時に「買い物カート」が必要です。でもこれはもともとのビジネスには存在しない処理ですよね。
このようにリアルなビジネスにはないけれど、課題を解決するために必要なものを定義するのがこの層になります。

Interface Adapters(Controllers, Gateways, Presenters)

図の緑色のレイヤーです。
ここでのInterfaceはプログラミングにおけるInterfaceのことではないので注意してください。
アダプターの集合というニュアンスが近いです。
入力、永続化、表示を担当するオブジェクトが所属します。
入力(Controller)...入力されたデータの加工(イメージ: ゲームコントローラー)
永続化(Gateway)...データの保存処理(イメージ: メモリーカード、DB)
表示(Presenter)...結果の表示(イメージ: ディスプレイ)
以下のサイトにわかりやすい表があった。 https://gist.github.com/mpppk/609d592f25cab9312654b39f1b357c60.

用語 意味
Gateway Frameworks&Driversからのデータを抽象化する。RepositoryやSQLHandlerなど。
Presenter InteractorからOutput DataをOutput Boundaryを経由して受け取り、それをViewに適した形にして返す
Controller Webサーバ等からデータを受け取り、Input Dataに変換してUse Case(Input Boundary)へ渡す

Frameworks & Drivers(Devices, Web, UI, ExternalInterfaces, DB)

図の水色のレイヤーです。
Webフレームワークやデータベース操作オブジェクトなどのコード。
詳細なコード、ギークなコード。
フロントエンドのUIなど。

それぞれの層の依存関係性

それぞれの層について理解できたところで、もう一度図を見てみます。
左の円の中に矢印があります。この矢印はUMLでいう依存の矢印です。 つまり、外側が内側に依存している構図となっています。
ここで、依存についておさらいです。
例えば、Use CasesEntitiesに単方向で依存しているとします。この時、Entitiesに変更が生じた際、Use CasesEntitiesに依存しているため、影響を受けます。
これが依存関係です。反対に、Use Casesに変更が生じた際、Entitiesは影響を受けず、変更する必要はないです。
このように外側の層の変更が内側の層に影響しないような状態を外側から内側への単方向の依存関係といいます。

クリーンアーキテクチャの目的

依存関係を単方向、かつ内部の層が外部の層に依存しないことが理解できたと思います。
つまり、実現したいこと(ビジネスロジック)が変わったら、使用するDBやUIは見直す必要はある。
でも、使用するDBやUIのような外部のものを変更するために内部のビジネスロジックにまで変更が及ぶような設計はおかしいよね。ってことです。
クリーンアーキテクチャのメリットとして、以下があります。 - テストがしやすい。 これが一番大きなメリットです。例えば以下のようなケース。 - フロントエンドのテスト
バックエンドとフロントエンドを同時に開発しており、フロントエンドのテストをしたいけど、バックエンドのコードはできていない。
この場合はInteractorをモックにすることで任意の例外を投げたりしてテストができます。 - バックエンドのテスト
例えば、DBの選定が終わっていないけどビジネスロジックを書きたい場合。
何を出力するかの部分をビジネスロジックに書いていて、何に出力するかは外側の層にあります。 何に出力するか、どう出力するかについてはビジネスロジックは知らなくていいので、モックにできます。
具体的には、本番ではDBに書きこむけれど、テストの際には一旦インメモリに書き出して確認する。みたいなことができます。 - 疎結合が実現できる。 つまり、外側の層だけ交換することが可能です。内側に依存していないのですから。

Use Cases(右下の図)を理解する

f:id:taisei_miyaji:20220321081732p:plain.
この図が何を表しているのか私は初見でわからなかったので解説しておきます。
この図はより具体的な実装例を示していると思ってください。
まず、白抜きの矢印は汎化、矢印は依存を表しています。 また、右上の<I>はinterfaceを表しています。 処理の流れをこの図を見ながら整理すると、
1. Controllerが入力データをUse Case Inout Portに渡す 2. Use Case Input Portの実態であるUse Case Interactorに処理を移譲される。 3. Use Case Interactorは処理を行った結果(出力データ)をUse Case Output Portに渡す 4. Use Case Outputの実態であるPresenterに処理を移譲される。 5. Presenterは表示を行う。

こういう構成にしておくことで、Use Case Interactorを介してデータを受け渡ししているのでモックにしやすい=テストがしやすい構成になります。

まとめ

  • そもそもアーキテクチャの役割って何?必要なの?
    • プログラミングは自由。何でもできる。でも、サービスを作る上では方針をある程度決めておくといろんな事象に対応しやすいし、スムーズに開発できるよね。っていうイメージ。
  • ヘキサゴナルアーキテクチャ、オニオンアーキテクチャとクリーンアーキテクチャは目指すところは同じ
  • 関心の分離を目指している。つまり交換しやすく、かつテストがしやすい(モックが作りやすい)。
  • 関心の分離の実現のために、各レイヤーは内側に向かってのみ依存する。内側の円は外側の円について何も知らない。
  • 依存関係逆転の原則を重要視している(SOLID原則の一つ。これについてはまたまとめます。)
    • 抽象は詳細に依存してはならない。詳細が抽象に依存すべきである。
    • 今回の場合は抽象=ビジネスロジック,詳細=外側の層、DBとか。
    • 要するに技術面での変更がビジネスロジックに変更与えちゃダメってこと。
  • デメリットもある。
    • クラスがめちゃくちゃ増える。(スタブを自動生成するようなツール作ったりして解決する)
    • 一見複雑に見える(経験の問題、フォルダの切り出しの問題。適切なグルーピングを行うことで複雑どころかわかりやすくなるはず。)
  • クリーンアーキテクチャとは、適切なグルーピングによってレイヤーを分けて、それらを疎結合にして、依存を一方通行にすれば、テスタブルかつメンテしやすい設計が実現できる、というアーキテクチャ