クリーンアーキテクチャという言葉はよく聞くけれど、「わかったようでわからない」という人が多いのではないでしょうか?

クリーンアーキテクチャを解説した記事は、探せばすぐに出てきますが、あまり本質について語られている記事は少ないように感じます。

そこで、この記事でそれを簡潔に語っていこうと思います。

アーキテクチャとは

まず、そもそも「アーキテクチャ」とは何でしょうか?簡潔に説明したいので、どんどん答えを書いていきます。

  • 目的
    • 壊れにくいソフトウェアを作る
    • チーム内で統一した方針でソフトウェアを作る
  • 実現方法
    • 部品 (※1) 同士の依存関係 (※2) を整える
    • 部品が果たす役割・責務を明確にする

(※1) 「部品って具体的に何?」と思われた方は、一旦、クラスだと思っていただければ大丈夫です。 (※2) 後述します。

めちゃくちゃシンプルに書くとこうなると思います。

依存関係とは

クリーンアーキテクチャの本質を知るためには、このセクションがめちゃくちゃ大事です!! 「そんなの知ってるわ」という方も、クリーンアーキテクチャについての理解が「モヤッ」としている方は読んでください。

依存関係には、二種類の依存関係があります。

物理的な依存関係

まずは、物理的な依存関係について説明します。これは、多くの人が最初に思い浮かぶ依存関係の方だと思われます。

すなわち、「どの部品 (クラスなど) がどの部品のことを知っているか」という関係性のことです。

(このドキュメントのサンプルコードは Kotlin で記述させていただきます。ご了承ください。)

class A {
    val b: B
}

class B {
    // ...
}

上記のサンプルでは、クラス A がクラス B を知っているので、これを「 A が B に依存している」と言います。

これが、物理的な依存関係です。 (超ざっくり)

意味的な依存関係

次に、意味的な依存関係について説明します。ここで、クリーンアーキテクチャの記事でよく出てくる同心円状の図を見て下さい。 (探せばすぐに出てくるのでここには載せません。笑)

ドメインとは

中心にあるのが、 ドメインモデル や エンティティ ではないでしょうか?その一つ外側にあるのが、 ビジネスルール や ユースケース ではないでしょうか?

それらの中心の二つは、本質を理解する上では、同じものとみなして問題ないため、これらをまとめて ドメイン と呼ぶことにします。

ドメインとは、簡単に言うと、ソフトウェアで実現したい中心的な機能のことです。

例えば、おみくじアプリなら、以下のようなものがあります。

  • くじを表すモデル
  • ユーザーがくじを引いたときに、どのくじを選ぶかのロジック

ドメイン以外

クリーンアーキテクチャの本質を理解する上では、ドメインかドメイン以外かだけを区別すれば大丈夫です。

ドメイン以外のものの例を少し上げておきます。

  • ユーザーインターフェース (つまり画面そのもの)
  • データベース
  • リポジトリ

同心円状の図の意味

同心円状の図を見ると、「ドメイン以外」のものから「ドメイン」へ向かっていく方向で、いくつもの矢印が書いてあるはずです。この矢印が意味的な依存関係を示しています。

意味的な依存関係がこうなっていると、ソフトウェアが壊れにくいよ!

めっちゃ簡単に言うと、この図が示している意味はそれだけです。

意味的な依存関係とクリーンアーキテクチャ

意味的な依存関係とは、コードには現れません。開発者の頭の中にだけ存在するものです。コードに現れるのは、物理的な依存関係だけです。

では、意味的な依存関係とは何なのか?

意味的な依存関係とは、「物理的な依存関係の本来あるべき姿」のことです。

「どの変更がどこに影響してほしいか/してほしくないか」によって、意味的な依存関係を定義します。

つまり、物理的な依存関係が A -> B となっているけれど、「 B の変更の影響は B に閉じていてほしい」という場合、意味的な依存関係は A <- B となります。

そして、本来あるべき依存関係を、ネットでよく見る「同心円状の図」のように思い描くことが クリーンアーキテクチャ とよばれる思想です。 これで、「クリーンアーキテクチャは思想だ」と言われる理由が分かったのではないでしょうか?

具体例

ここからは、具体例をあげていきます。

依存関係の逆転が必要なケース

多くのソフトウェアでは、

  • ユーザー入力
  • ビジネスロジックを実行
  • 結果を保存

という流れを持っているのではないでしょうか?

この場合、普通に実装すると

UI -> ドメイン -> リポジトリ

という物理的な依存関係になると思います。

しかし、思い出してください。 クリーンアーキテクチャでは、こう

ドメイン -> リポジトリ

ではなく、こういう依存関係にしたかったはずです。

リポジトリ -> ドメイン

では、どうすれば依存関係が逆転できるのか?

依存関係を逆転させる具体的な方法

逆転させる方法は、多くのエンジニアが知っている簡単な方法です。インターフェースを挟めば良いのです。

インターフェースを挟む前:

class Domain {
    val repository: Repository
}

class Repository {
    // ...
}

インターフェースを挟んだ後:

class Domain {
    val repository: Repository

    interface Repository { // (※ 1)
        // ...
    }
}

class RepositoryImpl: Repository {
    // RepositoryImpl は、具体的な実装を持っているため
    // インターフェースを挟む前の Repository に相当します。
}

(※ 1) 実際のプロジェクトでは、Repository インターフェースは Domain パッケージ直下などに 定義されることも多いですが、ここでは「ドメインの持ち物である」ことを強調するため、 この形で示しています。

こうすることで、 Domain は

  • 自分の持ち物である Repository インターフェースにだけ依存する
  • RepositoryImpl (リポジトリ本体) には依存していない

RepositoryImpl は

  • Domain の持ち物である Repository に依存する
  • そのため、実質的に Domain に依存しているのと同じ

という状況ができました。

つまり、物理的な依存関係が意味的な依存関係である

リポジトリ -> ドメイン

という関係性になりました。

「 Domain が Repository インターフェースに依存するのはいいのか?」 という声が聞こえてきそうですが、いいんです。 なぜなら、自分の持ち物であるから。

自分の持ち物であるインターフェースに変更が発生したら、自分自身に影響が出るのはあたり前だからいいんです。

問題なのは、自分に関係のない修正が発生した場合に、自分に影響が出ることです。

それが起きると、ちょっと修正しただけで、影響が爆発的に増えるソフトウェアになってしまいます。

おすすめの記事

この記事では、「なぜドメインが図の中心にくると壊れにくいのか」には触れておりません。

その理由はこちらの記事( なぜクリーンアーキテクチャはドメインを守るのか )で解説しております。

もし、気になる方は、あわせてご参考にしていただけますと幸いです。

おすすめの本(無料)

クリーンアーキテクチャは、依存関係の話であり、 その本質は「どこで判断を終わらせるか」という話でもあります。

この「判断」という視点で、 状態・イベント・境界・UI・ViewModel が どこまで判断し、どこから判断を持ち越さないかを整理したものを、 『設計の本質』 という本にまとめ、無料公開しています。

クリーンアーキテクチャが 「図としてはわかるが、実装になると迷う」 と感じていた方には、 補助線として役に立つはずですので、 あわせてご参考にしていただけますと幸いです。