改めて「architecture-samples usecase」ブランチからUseCaseの使い方を学んでみる

随分と久々の投稿になってしまいました。
前回の投稿から1年以上経過していますね。

今回は、「[Android Architecture Blueprints - Use Cases/Interactors in Domain layer](https://github.com/android/architecture-samples/tree/usecases)」を改めて読み込んでみました。

弊社アプリではオニオンアーキテクチャを採用していますが、
その文脈のDomain(or Application) ServiceにUseCaseを配置しようと試みています。
その参考のため、上記のGithubリポジトリが非常に参考になりそうでした。

※本投稿では、オニオンアーキテクチャとそれに関する用語は説明しません...m(_ _)m

# UseCaseはどの様に使われているか

結論は、ViewModelのコンストラクタ引数としてインジェクトされています。

```
class SampleViewModel(
    private val aaaUseCase: AaaUseCase,
    private val bbbUseCase: BbbUseCase,
    private val cccUseCase: CccUseCase
) : ViewModel() {

    fun loadAaa(forceUpdate: Boolean) {
        viewModelScope.launch {
            val result = aaaUseCase(forceUpdate)
            ...
        }
    }
}
```

```
class AaaUseCase(
    private val xxxRepository: XxxRepository
) {
    suspend operator fun invoke(
        forceUpdate: Boolean = false
    ): YYYY {
        ...
    }
```

なるほどですね。

# UseCaseはどうやってインスタンス化する？

ViewModelFactoryを使います。
上記サンプルでは、Applicationクラス生成時にRepositoryを生成します。
そして、FragmentでViewModelFactoryを生成する際に、Repositoryをコンストラクタ引数でインジェクトしています。

```
@Suppress("UNCHECKED_CAST")
class ViewModelFactory constructor(
    private val xxxRepository: XxxRepository
) : ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel> create(modelClass: Class<T>) =
            with(modelClass) {
                when {
                    isAssignableFrom(SampleViewModel::class.java) ->
                        SampleViewModel(
                            AaaUseCase(xxxRepository),
                            BbbUseCase(xxxRepository),
                            CccUseCase(xxxRepository)
                        )
                    else ->
                        throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
                }
            } as T
}
```

```
class SampleFragment : Fragment() {

    private val viewModel by viewModels<SampleViewModel> { getViewModelFactory() }
}

fun Fragment.getViewModelFactory(): ViewModelFactory {
    val repository = (requireContext().applicationContext as MyApplication).xxxRepository
    return ViewModelFactory(repository)
}
```

Repositoryをどうやってインスタンス化するかは、状況によるかと思います。
弊社では、Daggerを活用したServiceProvider的なクラスを用意しています。

# UseCaseはどのレイヤーに配置する？

Domain (or Application) Serviceに配置しようと思いました。
弊社アプリでは、Repository interfaceをDomain Servicesのレイヤーに配置していますが
同じ階層にUseCaseを配置しようと考えています。


# Junit testはどう書く？

上記ブランチでは、Repository, UseCase, ViewModel, Fragmentのテストを書いていました。
弊社では、Fragmentのテストは（今時点では）書かないかも知れませんが、それ以外は書いた方が良さそうだなと思います。

# UseCaseの使い所って、どんな”ユースケース”だろう？

シンプルなロジックの場合、UseCaseを使うとオーバースペックになる可能性があると感じます。
（ViewModelにRepositoryをインジェクトすれば済むケースもありそう）

UseCaseの使い所としては、例えば、以下の様な状況があり得そうだと感じました。

- Repositoryの複数メソッドを組み合わせて結果を得たい
- その他のDomain Service (or Application Service)を組み合わせて結果を得たい

# いかがでしたでしょうか

UseCaseが活用できるようになるには、ドメインを分析し、その結果をDomain Model / Domain Service クラスとして定義できている事が前提の様に感じました。
導入するには、周囲の人を巻き込んでいかないといけないですね。

弊社モバイルチームでは、週に1回DDDの勉強会を行なっていますので、今回の知見を元に更にブラッシュアップをしていこうと思います。


# 参照

- Android Architecture Blueprints - Use Cases/Interactors in Domain layer
  - https://github.com/android/architecture-samples/tree/usecases