随分と久々の投稿になってしまいました。
前回の投稿から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の勉強会を行なっていますので、今回の知見を元に更にブラッシュアップをしていこうと思います。
参照
- Android Architecture Blueprints - Use Cases/Interactors in Domain layer