Use this file to discover all available pages before exploring further.
Use cases encapsulate business logic and coordinate between repositories and the presentation layer. Each use case has a single responsibility and is injected into ViewModels via Hilt dependency injection.
class UseCaseNameUseCase @Inject constructor( private val repository: SomeRepository) { operator fun invoke(params): ReturnType = repository.operation(params)}
The operator fun invoke() allows calling the use case as a function:
class GetSurahListUseCase @Inject constructor( private val repository: QuranRepository) { operator fun invoke(): Flow<List<Surah>> = repository.getAllSurahs() fun byRevelationType(type: RevelationType): Flow<List<Surah>> = repository.getSurahsByRevelationType(type) fun search(query: String): Flow<List<Surah>> = repository.searchSurahs(query)}
Usage:
// Get all surahsgetSurahList()// Filter by revelation typegetSurahList.byRevelationType(RevelationType.MECCAN)// Search surahsgetSurahList.search("al-")
All Quran use cases are grouped in a data class for easy injection:
data class QuranUseCases( val getSurahList: GetSurahListUseCase, val getSurahWithAyahs: GetSurahWithAyahsUseCase, val getAyahsByJuz: GetAyahsByJuzUseCase, val getAyahsByPage: GetAyahsByPageUseCase, val getSajdaAyahs: GetSajdaAyahsUseCase, val searchQuran: SearchQuranUseCase, val getAvailableTranslators: GetAvailableTranslatorsUseCase, val toggleBookmark: ToggleQuranBookmarkUseCase, val getBookmarks: GetQuranBookmarksUseCase, val isAyahBookmarked: IsAyahBookmarkedUseCase, val updateBookmark: UpdateQuranBookmarkUseCase, val deleteBookmark: DeleteQuranBookmarkUseCase, val toggleFavorite: ToggleQuranFavoriteUseCase, val getFavorites: GetQuranFavoritesUseCase, val getFavoriteAyahIds: GetQuranFavoriteAyahIdsUseCase, val getReadingProgress: GetReadingProgressUseCase, val updateReadingPosition: UpdateReadingPositionUseCase, val incrementAyahsRead: IncrementAyahsReadUseCase, val getSurahInfo: GetSurahInfoUseCase, val getPageAyahRanges: GetPageAyahRangesUseCase)
class CreateKhatamUseCase @Inject constructor( private val repository: KhatamRepository) { suspend operator fun invoke(khatam: Khatam): Long = repository.createKhatam(khatam)}
data class KhatamUseCases( val createKhatam: CreateKhatamUseCase, val getActiveKhatam: GetActiveKhatamUseCase, val observeActiveKhatam: ObserveActiveKhatamUseCase, val setActiveKhatam: SetActiveKhatamUseCase, val markAyahsRead: MarkAyahsReadUseCase, val getReadAyahIds: GetReadAyahIdsUseCase, val observeReadAyahIds: ObserveReadAyahIdsUseCase, val getJuzProgress: GetJuzProgressUseCase, val observeDailyLogs: ObserveDailyLogsUseCase, val completeKhatam: CompleteKhatamUseCase, val abandonKhatam: AbandonKhatamUseCase, val reactivateKhatam: ReactivateKhatamUseCase, val deleteKhatam: DeleteKhatamUseCase, val observeAllKhatams: ObserveAllKhatamsUseCase, val observeInProgressKhatams: ObserveInProgressKhatamsUseCase, val observeCompletedKhatams: ObserveCompletedKhatamsUseCase, val observeAbandonedKhatams: ObserveAbandonedKhatamsUseCase, val observeKhatamById: ObserveKhatamByIdUseCase, val logDailyProgress: LogDailyProgressUseCase, val getKhatamStats: GetKhatamStatsUseCase, val getNextUnreadPosition: GetNextUnreadPositionUseCase, val unmarkAyahRead: UnmarkAyahReadUseCase, val markSurahAsRead: MarkSurahAsReadUseCase)
Each use case should do one thing. Don’t combine multiple repository calls in one use case unless they represent a single business operation.Good:
class ToggleBookmarkUseCase @Inject constructor( private val repository: QuranRepository) { suspend operator fun invoke(ayahId: Int, surahNumber: Int, ayahNumber: Int) { repository.toggleBookmark(ayahId, surahNumber, ayahNumber) }}
Bad:
class BookmarkAndUpdateProgressUseCase @Inject constructor( private val repository: QuranRepository) { suspend operator fun invoke(ayahId: Int, surahNumber: Int, ayahNumber: Int) { repository.toggleBookmark(ayahId, surahNumber, ayahNumber) repository.updateReadingPosition(surahNumber, ayahNumber, 0, 0) repository.incrementAyahsRead(1) }}
Use Flow for reactive data
For data that changes over time, return Flow instead of suspend functions.Good:
class ObserveActiveKhatamUseCase @Inject constructor( private val repository: KhatamRepository) { operator fun invoke(): Flow<Khatam?> = repository.observeActiveKhatam()}
Bad:
class GetActiveKhatamUseCase @Inject constructor( private val repository: KhatamRepository) { suspend operator fun invoke(): Khatam? = repository.getActiveKhatam()}
(Note: Both patterns exist in the codebase for one-time vs. reactive needs)
Keep use cases thin
Use cases should primarily delegate to repositories. Complex business logic should be in the repository implementation or domain models.Good:
class GetJuzProgressUseCase @Inject constructor( private val repository: KhatamRepository) { suspend operator fun invoke(khatamId: Long): List<JuzProgressInfo> = repository.getJuzProgress(khatamId)}
Bad (calculation in use case):
class GetJuzProgressUseCase @Inject constructor( private val repository: KhatamRepository) { suspend operator fun invoke(khatamId: Long): List<JuzProgressInfo> { val readAyahs = repository.getReadAyahIds(khatamId) return (1..30).map { juzNumber -> val totalAyahs = calculateJuzAyahCount(juzNumber) val readCount = readAyahs.count { isInJuz(it, juzNumber) } JuzProgressInfo(juzNumber, totalAyahs, readCount) } }}