依存性逆転の原則って、結局何が逆転してるの？

[bitFlyer Advent Calendar 2022](https://qiita.com/advent-calendar/2022/bitflyer)の2日目です。
こんにちは、私はフロントエンド開発部でAndroidエンジニアをしています。

タイトルは、`依存性逆転の原則`に対する私のファーストインプレッションです。

依存性逆転の原則とはSOLID原則の一つです。( `Dependency Inversion Principle` )
これを調べると大抵 __具体に依存せずにインターフェース（抽象）に依存すべきという原則__ という説明がなされています。
合っているとは思うのですが、これだけだと「抽象に依存すべきというのはわかったけど、結局何が逆転したの？？」という疑問が残りますよね。
ここをわかりやすく解説するのが本記事の目的です。


### 目次
[1.素直に実装してみる](#1-素直に実装してみる)
[2.interfaceを使って抽象化してみる](#2-interfaceを使って抽象化してみる)
[3.結局どこが逆転していたのか](#3-結局どこが逆転していたのか)
[4.おわりに](#4-おわりに)

# 1. 素直に実装してみる

カウンターアプリを例に考えていきます。
プラスボタンがあって、押すたびにカウントアップされていくだけのアプリだと思ってください。（Flutterを書いてる人は確実に見覚えがあるアレです）

カウントアップしていくだけなのですごくシンプルとはいえ、考えることはあります。
1. インクリメントした数字をどう保存するか
1. 数字をインクリメントするロジックはどこに書くか

などなど。
まず、1. から。
そのカウントされた数字はどうしましょう？
リモートDBに保存しますか？
それともローカルDBで良いでしょうか。
決まらないので、とりあえずin-memoryでRepositoryに保持しておくことにします。

```kotlin: InMemoryCounterRepository
class InMemoryCounterRepository {
    private val _counter = MutableStateFlow(0)

    fun saveCount(count: Int) {
        _counter.update {
            count
        }
    }
}
```

カウントアップするUseCaseも定義しちゃいましょう

```kotlin: IncrementCountUseCase
class IncrementCountUseCase(
    private val repository: InMemoryCounterRepository,
) : AbstractUseCase<Int, Unit>() {
    override fun execute(params: Int) {
        repository.saveCount(params + 1)
    }
}
```

RepositoryとUseCaseを定義しました。

これらをクラス図にすると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367893/2e339f01-6801-c4ed-6ea1-e48c97cb5249.png)

`UseCase`は`Repository`に依存しています。

とても素直です。

ところが、です。
「誰でもそのカウンターの値を見られるようにしたいから、数字をリモートDBにも送って欲しい！！」という要求が突如として湧いてきたとします。（なんだそりゃ！？、という気持ちは抑えてください）
困りましたね・・・


# 2. interfaceを使って抽象化してみる

こんなときスマートに`Repository`を置き換える方法があります。
依存性逆転の原則を利用します。


interfaceを用います。
```kotlin:CounterRepository
interface CounterRepository {
    fun saveCount(count: Int)
}
```

上記のinterfaceを、先ほどの`InMemoryCounterRepository`に継承させて
```kotlin:InMemoryCounterRepositoryImpl
class InMemoryCounterRepositoryImpl : CounterRepository {
    private val _counter = MutableStateFlow(0)

    override fun saveCount(count: Int) {
        _counter.update {
            count
        }
    }
}
```

上記の実装クラスを作成します。

そして`IncrementCountUseCase`のコンストラクタの`Repository`の型を`CounterRepository`に修正すると、クラス図は下記のように修正されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367893/caebfc82-38b7-961f-3cb8-89f46c4b4f58.png)


すごい！！！
よく見るやつです！

Repositoryの置き換えをスマートにやれますね。自作でMockクラスを作って返す値をコントロールしたりもしやすいのでとても便利です。
下記のような実装クラスを`InMemoryCounterRepository`の代わりにインジェクトすれば「カウントアップした数字をリモートDBに保持したい！」いう新たな要件を満たせそうです。

```kotlin:APIRepositoryImpl
class APIRepositoryImpl @Inject constructor(private val client: APIClient) : CounterRepository {
    override fun saveCount(count: Int) {
        client.updateCount(count)
    }
}

```


無事に依存性が逆転しました！！！！

めでたしめでたし



・・・・とは私は思えませんでした。

 __「`UseCase`は引き続き`Repository`に依存してるように見えるけど、結局どこが逆転してるの？？」__ というのが率直な感想でした。



# 3. 結局どこが逆転していたのか

もう少しマクロな視点で見たらわかってきます。
## 1. interfaceを使う前のクラス図

`Repository`と`UseCase`のレイヤーを分けてみましょう。（レイヤーを分ける、というのはここではpackageを分けるくらいのイメージで大丈夫です）

![スクリーンショット 2022-11-18 20.03.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367893/7bea775e-802c-b92b-a646-2da48d218bd1.png)

結論として、`UseCase`はDomain層に、`Repository`はData層に分けます。（UseCaseにはロジックがあり、RepositoryはDataを扱うためです）
冒頭であった、 __2. インクリメントのロジックをどこに置くか__ という話ですが、`UseCase`に書いておくととても良いと思います。
なぜなら、`Repository`側にロジックを漏らすと、Repositoryを差し替えたときにそのロジックを共通で使えません。※1
上記の図の矢印を見ると、依存関係は `Domain -> Data` となっていますね。
実際にソースコードのimportの向きもこうなっています。

（※1 若干思想的な話になるのですが、よく聞かれる「UseCaseはRepositoryのメソッドを呼び出すだけで空っぽだから不要」という思想は一理あると思います。しかし私個人としては前述の理由で`UseCase`はあった方が良いと考えています。）

## 2. interfaceを使った後のクラス図

次に、interfaceを使った場合のクラス図のレイヤーを分けてみましょう。

![スクリーンショット 2022-11-18 20.07.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367893/da0feeae-56d8-362d-56ba-6684735d04ad.png)

境界線を跨ぐ矢印の方向を見てください！！

さっきとは逆を向いていますね。

`Data -> Domain`に __依存の方向が逆転__ しています！！
ここにきて初めて何が逆転しているのか理解しました。
境界を越える矢印の向きが逆転していたんだ、と。
実際に図の通りにdomainとdataでディレクトリを分けると、この矢印の向きはimportの向きとも一致します。
（※実装クラスをDomain層でインスタンス化するとDomainのパッケージがDataのパッケージに依存してしまいimportの向きが狂ってしまうので、DIコンテナを使うことでうまく避けられます）

`Domain層 -> Data層`だった依存の向きを`Data層 -> Domain層`と逆を向かせて何が嬉しいのか、というと
- Domain層がData層に依存しないのでDomain層を安定させられる（ 例えばData層のライブラリを変更したり、レスポンスのプロパティが変更したときに都度Domain層に変更を入れずに済む ）
- Domainを切り出してマルチモジュール化すると、依存性を強制させられる（ 循環依存したりレイヤーの崩壊を避けやすい ）

などなど
まとめると、変更に強く秩序あるコードにできる、という感じです。


胸のつっかえがとれましたね:joy:（とれていない方はガンガンコメントください！）



# 4. おわりに

「一体何が逆転しているんだ？？？」と胸につっかえを抱えている人が少しでもスッキリすれば良いなという願いがあり当記事を執筆しました。
実際にレイヤーを分けてマルチモジュール化した[サンプルコード](https://github.com/yoshimuratakuma0/counter)を置いておきます。
ぜひ参考にしてください！

また、弊社bitFlyerでは[Androidエンジニアを募集](https://www.green-japan.com/company/3509/job/167514)しています。ご興味のある方はぜひご応募ください！