REDI-94: GetCharactersPageUseCase + inject into list ViewModel
Add a domain UseCase (operator invoke) in :feature:characters:domain delegating to CharacterRepository, and have CharacterListViewModel depend on it instead of the repository directly. The UseCase is a deliberate thin pass-through that documents the 'when to add a UseCase' convention (real logic / multi-source composition vs. a single forwarded call).
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.example.architecture.feature.characters.domain.usecase
|
||||
|
||||
import com.example.architecture.core.domain.DataError
|
||||
import com.example.architecture.core.domain.Result
|
||||
import com.example.architecture.feature.characters.domain.CharacterRepository
|
||||
import com.example.architecture.feature.characters.domain.model.CharactersPage
|
||||
|
||||
/**
|
||||
* Loads one page of characters.
|
||||
*
|
||||
* **When to add a UseCase (convention note):** introduce a UseCase when a screen needs business
|
||||
* logic that does NOT belong in the ViewModel — non-trivial rules, or *composition* of several
|
||||
* repositories/sources into one domain operation. When the ViewModel would merely forward a single
|
||||
* repository call, skipping the UseCase and injecting the repository directly is perfectly fine.
|
||||
*
|
||||
* This particular UseCase is a **thin pass-through, included for illustration**: it adds no logic
|
||||
* beyond delegating to [CharacterRepository]. It earns its place only as a showcase of the
|
||||
* convention (domain-owned, `operator fun invoke`, constructor-injected). In a real app you would
|
||||
* grow it the moment list loading gained real behaviour (filtering, merging a local cache, …) — or
|
||||
* delete it and let the ViewModel call the repository.
|
||||
*/
|
||||
class GetCharactersPageUseCase(
|
||||
private val characterRepository: CharacterRepository,
|
||||
) {
|
||||
suspend operator fun invoke(page: Int): Result<CharactersPage, DataError> =
|
||||
characterRepository.getCharacters(page)
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import com.example.architecture.core.domain.onFailure
|
||||
import com.example.architecture.core.domain.onSuccess
|
||||
import com.example.architecture.core.presentation.UiText
|
||||
import com.example.architecture.core.presentation.toUiText
|
||||
import com.example.architecture.feature.characters.domain.CharacterRepository
|
||||
import com.example.architecture.feature.characters.domain.usecase.GetCharactersPageUseCase
|
||||
import com.example.architecture.feature.characters.presentation.model.CharacterUi
|
||||
import com.example.architecture.feature.characters.presentation.model.toCharacterUi
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -25,7 +25,7 @@ import kotlinx.coroutines.launch
|
||||
* via a [Channel], maps failures to [UiText], and persists the loaded page in [SavedStateHandle].
|
||||
*/
|
||||
class CharacterListViewModel(
|
||||
private val characterRepository: CharacterRepository,
|
||||
private val getCharactersPage: GetCharactersPageUseCase,
|
||||
private val savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
@@ -62,7 +62,7 @@ class CharacterListViewModel(
|
||||
|
||||
var page = 1
|
||||
while (page <= targetPage) {
|
||||
when (val result = characterRepository.getCharacters(page)) {
|
||||
when (val result = getCharactersPage(page)) {
|
||||
is Result.Success -> {
|
||||
accumulated += result.data.characters.map { it.toCharacterUi() }
|
||||
lastLoadedPage = page
|
||||
@@ -123,7 +123,7 @@ class CharacterListViewModel(
|
||||
// get appended twice.
|
||||
_state.update { it.copy(isLoadingNextPage = true, error = null) }
|
||||
viewModelScope.launch {
|
||||
characterRepository.getCharacters(page)
|
||||
getCharactersPage(page)
|
||||
.onSuccess { pageData ->
|
||||
_state.update { state ->
|
||||
state.copy(
|
||||
|
||||
Reference in New Issue
Block a user