This project demonstrates a modern scalable architecture for Android apps built with Jetpack Compose.
It is a simple List/Detail application that showcases how to structure code using Clean Architecture and MVI with the latest Android libraries.
The project follows Clean Architecture with MVI (Model–View–Intent) for state management.
- Core
Shared modules fordata,domain, andpresentation. - Feature modules (e.g.
sampleFeature)
Each feature contains its owndata,domain, andpresentationpackages. - Navigation handled with Navigation Compose.
- Dependency Injection with Hilt.
- Database with Room as the single source of truth.
- Networking with Ktor Client (currently mocked API).
- UI: Jetpack Compose, Material 3, Previews
- State Management: MVI + ViewModels + StateFlow + sealed classes
- Navigation: Navigation Compose (type-safe routes)
- Coroutines + Flow for async/reactive handling
- DI: Hilt
- Networking: Ktor (mocked)
- Persistence: Room Database
- Gradle: Version catalog (
libs.versions.toml) with Kotlin DSL
app/
┣ core/
┃ ┣ data/ # Shared data layer (api, db, repository base)
┃ ┣ domain/ # Domain layer (models, use cases, repository interfaces)
┃ ┣ presentation/ # Shared presentation utilities
┃
┣ di/ # Hilt modules
┣ navigation/ # Navigation graph & routes
┣ sampleFeature/ # Example feature module (list/detail)
┃ ┣ data/
┃ ┣ domain/
┃ ┣ presentation/
┃
┣ ui/ # App-wide UI components (themes, design system)
┣ MainActivity.kt # Entry point
┗ MyApp.kt
Here’s how the ItemList feature works:
- UI sends an
ItemListActionto theViewModel. - ViewModel updates its
StateFlow<ItemListUIState>. - UI collects state via
collectAsStateWithLifecycle(). - Errors or one-time messages are sent via
events: Flow<ItemListEvents>. HandleEvents()composable observes and renders dialogs/snackbars.
Example:
val state by viewModel.state.collectAsStateWithLifecycle()
HandleEvents(events = viewModel.events)
ItemListScreen(
state = state,
onAction = { action -> viewModel.onAction(action) }
)The Repository is the central point that merges remote and local data sources.
It ensures that the Room database is the single source of truth.
- Fetches list of items from
ItemApi(Ktor-based, currently mocked) - Persists data in
ItemDao(Room database) - Provides reactive streams via
Flow<List<Item>>so the UI can observe changes
override fun observeItemList(): Flow<List<Item>> {
return itemDao.observeAll().map { entities -> entities.map { it.toDomain() } }
}- Clean Architecture + MVI in Jetpack Compose
- Single source of truth via Room DB
- Repository pattern merging API + persistence
- Sealed classes for state, actions, and events
- Scalable feature-based packaging
MIT License – free to use and modify.