1
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-01-11

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

今回は、「Android Architecture Blueprints - Use Cases/Interactors in Domain layer」を改めて読み込んでみました。

弊社アプリではオニオンアーキテクチャを採用していますが、
その文脈の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の勉強会を行なっていますので、今回の知見を元に更にブラッシュアップをしていこうと思います。

参照

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0