diff --git a/feature/characters/domain/src/main/kotlin/com/example/architecture/feature/characters/domain/usecase/GetCharactersPageUseCase.kt b/feature/characters/domain/src/main/kotlin/com/example/architecture/feature/characters/domain/usecase/GetCharactersPageUseCase.kt new file mode 100644 index 0000000..fb87121 --- /dev/null +++ b/feature/characters/domain/src/main/kotlin/com/example/architecture/feature/characters/domain/usecase/GetCharactersPageUseCase.kt @@ -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 = + characterRepository.getCharacters(page) +} diff --git a/feature/characters/presentation/src/main/kotlin/com/example/architecture/feature/characters/presentation/CharacterListViewModel.kt b/feature/characters/presentation/src/main/kotlin/com/example/architecture/feature/characters/presentation/CharacterListViewModel.kt index c131025..e70c400 100644 --- a/feature/characters/presentation/src/main/kotlin/com/example/architecture/feature/characters/presentation/CharacterListViewModel.kt +++ b/feature/characters/presentation/src/main/kotlin/com/example/architecture/feature/characters/presentation/CharacterListViewModel.kt @@ -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(