Chapter 5 - Coding the first Use case (Domain layer)
The use case GetArtUseCase is the first use case we will code.
This use case will fetch a list of images from the Room DB locally. This list will help us to carve out the first screen displayed to the end user. The screen composable will be coded in a future chapter. We are following the clean architecture and will code the domain layer classes first associated with this use case. The domain layer is not dependent on Room or Retrofit or any other Android dependency. Since we are retrieving Art objects, the first domain model to code is the Art model class.
Data Flow for GetArtUseCase
Again going back to our mental model from Chapter 3, we see that the data flow happens this way :
By “backtracking” through the clean architecture flow (refer to the above flowchart), we begin with defining our core domain models and derive the corresponding use cases such as GetArtUseCase. The remaining use cases follow the same pattern. Once the domain layer is stable, we implement the data layer (Room, Retrofit, mappers) that satisfies the repository contracts. After that, we write unit tests to validate our business rules and ensure data flow correctness. Finally, we build the presentation layer with ViewModels and composables. Each piece fits naturally like a jigsaw puzzle, and upcoming blog posts will walk through this evolution step by step.
Data class Art
package com.learning.artsnapapp.domain.model
/**
* Domain model representing an artwork within the app's core business logic.
*
* This is a pure Kotlin data class and forms part of the Domain layer.
* It is intentionally kept independent of any Android or framework-specific code.
*
* Each property reflects essential metadata about an artwork,
* ensuring the model remains simple, testable, and decoupled from persistence or UI concerns.
*/
data class Art(
val id: Int = 0,
val name: String,
val artistName: String,
val year:String,
val imageUrl:String
)
The Art model is a pure Kotlin data class with no framework dependencies, ensuring the domain layer remains platform-agnostic.
Now we write the interface code.
Art Repository Interface
package com.learning.artsnapapp.domain.repository
import com.learning.artsnapapp.domain.model.Art
import com.learning.artsnapapp.domain.model.ImageResult
import com.learning.artsnapapp.util.Resource
import kotlinx.coroutines.flow.Flow
/**
* Repository abstraction that defines all data operations related to artworks.
*
* This interface acts as a bridge between the Domain and Data layers.
* It hides implementation details (e.g., Room database, Retrofit API)
* from the domain logic, promoting dependency inversion and testability.
*
* Implementations (like ArtRepositoryImpl) will provide the actual logic
* to interact with local and remote data sources.
*/
interface ArtRepositoryInterface {
fun getArt(): Flow<List<Art>>
fun insertArt(art:Art)
fun deleteArt(art:Art)
fun searchImage(userQuery:String):Flow<Resource<List<ImageResult>>>
}
/**
The domain layer defines what data is needed, not how it is fetched.
The data layer will implement this interface.
* */
The ArtRepositoryInterface will be updated later with the remaining functions. The data layer will implement this interface.
The repository interface:
expresses pure business-level data needs (insert art, delete art, get art, search images)
hides the details of whether data comes from Room, Retrofit, cache, file system, etc.
creates a contract between domain and data layers
allows the domain to be testable without a real database or network.
The repository interface declares the data operations required by the business logic while decoupling the domain from database and network frameworks. It acts as an abstraction that keeps the domain pure, testable, and framework-agnostic.
Just keep a close “watch” on the package names and import statements. The domain layer classes have no import statements from the presentation and data layers. They are independent and hence easily testable.
Now we code the GetArtUseCase class
GetArtUseCase
package com.learning.artsnapapp.domain.usecase
import com.learning.artsnapapp.domain.model.Art
import com.learning.artsnapapp.domain.repository.ArtRepositoryInterface
import kotlinx.coroutines.flow.Flow
/**
* Use case for retrieving all artworks from the repository.
*
* This encapsulates the business rule for fetching locally stored artworks.
* The class depends only on the repository interface, not its implementation,
* making it easy to mock and unit test.
*
* The operator function `invoke()` enables the use case to be called as a function.
* It emits a Flow<List<Art>> stream, allowing the UI layer to reactively observe data changes.
*/
class GetArtUseCase(private val artRepository: ArtRepositoryInterface) {