UseCase Usages and Best Practices
Explore UseCase Usages and Best Practices using Kotlin

Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.
Search for a command to run...
Explore UseCase Usages and Best Practices using Kotlin

Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.
No comments yet. Be the first to comment.
A practical guide to choosing the right coroutine primitive for your Android/Kotlin projects

This guide explains what Channels are, when to use them, how to use them correctly, and when NOT to use them β in simple, professional language. 1. What problem do Channels solve? In Kotlin coroutines, you often have multiple coroutines running at t...

Learn how to build preview-safe ViewModels in Jetpack Compose using Hilt.

HandlerInterceptor vs Filter in Spring Boot: A Practical Guide with JNDI Injection Prevention

Simple In-Memory Caching: A Tiny Trick with Massive Impact

The UseCase pattern is not a standalone design pattern in the traditional sense like Observer or Strategy patterns. Instead, it's a concept that is commonly used within the context of software architecture, particularly in architectures like Clean Architecture and Domain-Driven Design (DDD).
In essence, a UseCase represents a single, specific task or action that the system needs to perform. It encapsulates the business logic necessary to fulfill that task. UseCases are often used to separate the business logic from the rest of the application, promoting modularity, testability, and maintainability.
While UseCase itself may not be considered a standalone design pattern, it embodies principles of separation of concerns and single responsibility, which are core tenets of good software design. It's more of a structural and organizational concept rather than a formal design pattern.
In this article we will delves into the principles and practices of clean architecture, focusing particularly on the role and characteristics of UseCases.
Responsibility of UseCase:
UseCase encapsulates business logic for a single reusable task.
It should focus on what the product team needs to achieve.
Example: A payment UseCase performing tasks like starting a transaction, sending payment, and finalizing the transaction.
class SendPayment(private val repo: PaymentRepo) {
suspend operator fun invoke(amount: Double, checkId: String): Boolean {
val transactionId = repo.startTransaction(params.checkId)
repo.sendPayment(amount = params.amount, checkId = params.checkId, transactionId = transactionId)
return repo.finalizeTransaction(transactionId)
}
}
Key Points on UseCase:
// Example of a UseCase performing a single task
class SaveImageUseCase @Inject constructor(/* dependencies */) {
operator fun invoke(file: File): Single<Boolean> { /* logic for saving image */ }
}
// Example of ensuring thread safety in a UseCase
class AUseCase @Inject constructor(
@IoDispatcher private val dispatcher: CoroutineDispatcher,
) {
suspend operator fun invoke(): List<String> = withContext(dispatcher) {
val list = mutableListOf<String>()
repeat(1000) { list.add("Something $it") }
list.sorted()
}
}
Common Questions:
Abstraction with UseCases is optional unless multiple implementations are needed.
// Example of an abstract UseCase interface and its implementation
interface GetSomethingUseCase {
suspend operator fun invoke(): List<String>
}
class GetSomethingUseCaseImpl(private val repository: ChannelsRepository) : GetSomethingUseCase {
override suspend operator fun invoke(): List<String> = repository.getSomething()
}
UseCases can be redundant if they only wrap repository functions, but they can offer benefits in terms of code clarity, consistency, and future-proofing.
// Example of a UseCase wrapping a repository function
class GetSomethingUseCase @Inject constructor(private val repository: ChannelsRepository) {
suspend operator fun invoke(): List<String> = repository.getSomething()
}
// Example of using one UseCase within another
class CompositeUseCase @Inject constructor(
private val getSomethingUseCase: GetSomethingUseCase,
private val saveSomethingUseCase: SaveSomethingUseCase
) {
suspend operator fun invoke() {
val data = getSomethingUseCase()
saveSomethingUseCase(data)
}
}
Overall, the article provides insights into how to effectively structure and implement UseCases within clean architecture, highlighting the importance of adherence to single responsibility and clarity, while also addressing common concerns and providing practical examples.
That's it for today. Happy coding...