On this page
A use case (also called an interactor) is a class that performs one business operation, such as "place order" or "get visible articles", typically by combining repositories and applying rules. Use cases can make code clearer and more reusable, or they can be empty wrappers that double the number of files without adding meaning.
The three-question test
Create a use case when at least one of these is true:
- Real business logic exists beyond fetching data: rules, calculations or validation that the product team would describe ("orders over βΉ500 get free delivery unlessβ¦").
- Reuse: the same logic is needed by several ViewModels, a widget, a notification handler or a worker.
- Combination: the operation coordinates several repositories, such as user + cart + pricing.
If none apply, call the repository directly from the ViewModel.
Writing a good use case
data class DeliveryQuote(val feePaise: Long, val freeDeliveryShortfallPaise: Long)
class CalculateDeliveryFeeUseCase @Inject constructor(
private val cartRepository: CartRepository,
private val userRepository: UserRepository,
) {
/** Free delivery above βΉ500 for regular users; always free for members. */
operator fun invoke(): Flow<DeliveryQuote> =
combine(cartRepository.observeCart(), userRepository.observeUser()) { cart, user ->
val subtotal = cart.items.sumOf { it.unitPaise * it.qty }
when {
user.isMember || subtotal >= FREE_THRESHOLD -> DeliveryQuote(0, 0)
else -> DeliveryQuote(STANDARD_FEE, FREE_THRESHOLD - subtotal)
}
}
private companion object {
const val FREE_THRESHOLD = 50_000L // βΉ500 in paise
const val STANDARD_FEE = 4_000L // βΉ40
}
}
// In a ViewModel: val quote = calculateDeliveryFee().stateIn(viewModelScope, WhileSubscribed(5000), null)- One public operation, often exposed via
operator fun invokeso callers writecalculateDeliveryFee(). - Named as verb + noun:
PlaceOrderUseCase,GetVisibleArticlesUseCase. - Pure Kotlin, with no Android types. Main-safe, because threading is handled in repositories.
- Returns domain models or typed results, never UI strings or colours.
Testing
@Test
fun `members always get free delivery`() = runTest {
val useCase = CalculateDeliveryFeeUseCase(
FakeCartRepository(items = listOf(CartItem("a", unitPaise = 10_000, qty = 1))),
FakeUserRepository(User(isMember = true)),
)
assertEquals(DeliveryQuote(0, 0), useCase().first())
}The pass-through problem
class GetUserUseCase @Inject constructor(private val repo: UserRepository) {
operator fun invoke(id: String) = repo.getUser(id) // just forwards: delete it
}Some teams mandate use cases everywhere for consistency, so ViewModels never touch repositories. That's a legitimate team choice in very large codebases, but weigh the cost: more files, more indirection, slower navigation. Google's guidance treats the domain layer as optional.
Where use cases live
- Small app: a
domainpackage inside the feature. - Modular app: a pure-Kotlin
:core:domainor:feature:x:domainmodule, where the compiler enforces "no Android" automatically.
Good candidates in real apps
Checkout validation
Stock, address, payment and coupon rules combined into a ValidateCheckoutUseCase used by the screen and by a background retry.
Feed ranking
Filtering muted authors, pinning and sorting, shared by the feed screen and a widget.
Sync orchestration
A SyncAllUseCase calling several repositories in order, used by WorkManager and pull-to-refresh.
Frequently asked questions
Can a use case call another use case?
Yes, when composing operations makes sense. Avoid deep chains that are hard to follow.
Should use cases switch dispatchers?
Only if they do heavy CPU work themselves, using withContext(Dispatchers.Default) with an injected dispatcher. Otherwise rely on main-safe repositories.
Use case or extension function?
For small, pure transformations without dependencies, a function or extension can be simpler than a class.
Conclusion
Use cases are valuable for real, reusable or cross-repository business logic, and noise when they only forward calls. Keep them single-purpose, pure Kotlin and easy to test, and let simple screens talk to repositories directly.
Next step: expose the results cleanly to the UI with Managing UI State with ViewModel and StateFlow.
Official references
π± Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
Walzi4K wallpapers for lock and home screenGet it βΈ
NotiVaultNotification history: recover dismissed notificationsGet it βΈ
Ready to build real Android applications?
Architecture makes the most sense when you apply it to a real project and see why each boundary exists. Learn how maintainable Android applications are designed by building one.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.