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.domain.onSuccess
|
||||||
import com.example.architecture.core.presentation.UiText
|
import com.example.architecture.core.presentation.UiText
|
||||||
import com.example.architecture.core.presentation.toUiText
|
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.CharacterUi
|
||||||
import com.example.architecture.feature.characters.presentation.model.toCharacterUi
|
import com.example.architecture.feature.characters.presentation.model.toCharacterUi
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
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].
|
* via a [Channel], maps failures to [UiText], and persists the loaded page in [SavedStateHandle].
|
||||||
*/
|
*/
|
||||||
class CharacterListViewModel(
|
class CharacterListViewModel(
|
||||||
private val characterRepository: CharacterRepository,
|
private val getCharactersPage: GetCharactersPageUseCase,
|
||||||
private val savedStateHandle: SavedStateHandle,
|
private val savedStateHandle: SavedStateHandle,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ class CharacterListViewModel(
|
|||||||
|
|
||||||
var page = 1
|
var page = 1
|
||||||
while (page <= targetPage) {
|
while (page <= targetPage) {
|
||||||
when (val result = characterRepository.getCharacters(page)) {
|
when (val result = getCharactersPage(page)) {
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
accumulated += result.data.characters.map { it.toCharacterUi() }
|
accumulated += result.data.characters.map { it.toCharacterUi() }
|
||||||
lastLoadedPage = page
|
lastLoadedPage = page
|
||||||
@@ -123,7 +123,7 @@ class CharacterListViewModel(
|
|||||||
// get appended twice.
|
// get appended twice.
|
||||||
_state.update { it.copy(isLoadingNextPage = true, error = null) }
|
_state.update { it.copy(isLoadingNextPage = true, error = null) }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
characterRepository.getCharacters(page)
|
getCharactersPage(page)
|
||||||
.onSuccess { pageData ->
|
.onSuccess { pageData ->
|
||||||
_state.update { state ->
|
_state.update { state ->
|
||||||
state.copy(
|
state.copy(
|
||||||
|
|||||||
Reference in New Issue
Block a user