A clean, modular, and testable Jetpack Compose + Kotlin Inject template designed for building scalable Android apps using modern architectural patterns like MVI, DI, Middleware, and Reducer separation.
┌─────────────┐
│ Composable │
│ (UI Layer) │◄───── Collects ─────┐
└────┬────────┘ │
│ │
▼ │
┌───────────────┐ Emits │
│ ViewModel │─────────────────► SideEffect (e.g., NavigateToDetail)
│ (MVI entry) │ │
└────┬──────────┘ │
│ dispatches ▼
▼ ┌─────────────┐
┌───────────────┐ │NavController│
│ Delegate │ └─────────────┘
│ (MVI Engine) │
└────┬──────┬───┘
│ │
│ │
▼ ▼
┌────────┐ ┌────────────────────────────┐
│Reducer │ │ Middleware │
│(Sync) │ │(Async: API, Storage, etc.) │
└────────┘ └────────────────────────────┘
- Collects state with
collectAsStateWithLifecycle - Collects side effects via
flowWithLifecycle - Sends user events to
ViewModel.onAction(...)
- Delegates to
MVIengine (MVIDelegate) - Triggers initial actions like
FetchNews - Exposes state (
StateFlow) and side-effects (Flow) - DI-friendly using
@Inject,@Assisted, etc.
- Central coordinator
- Calls reducer first
- Then calls middleware(s) after state update
- Emits side-effects (e.g., navigation or toasts)
- Pure, synchronous state transition
- No side-effects beyond
Effect?return - Fully testable and predictable
- Handles asynchronous operations
- Network calls
- Database access
- Logging
- Never updates state directly — only dispatches actions back to the delegate
| Concern | Implementation |
|---|---|
| Async Handling | CoroutineScope + Middleware |
| Lifecycle-aware | collectAsStateWithLifecycle, flowWithLifecycle |
| Testability | Reducer & Middleware testable in isolation |
| DI Friendly | Uses @Inject, @Assisted, Set<Middleware> |
| Navigation | Triggered via SideEffect.NavigateToDetail(...) |
| Error Handling | AppError, CoroutineExceptionHandler inside Middleware |
| Threading | AppCoroutineDispatchers abstracts IO/Main/etc. |
| Scalability | Middleware is a Set = easily extendable |
| Category | Feature | Status |
|---|---|---|
| 🔌 Dependency Injection | Kotlin Inject with @Component, @Provides, @IntoSet |
✅ Completed |
| 🎨 UI | Jetpack Compose Screens (NewsList, NewsDetail) |
✅ Completed |
| 🧱 Navigation | Navigation with AppRouteFactory injection per feature |
✅ Completed |
| 🧠 State Management | MVI pattern (Reducer + Middleware + Delegate) | ✅ Completed |
| 🌐 Networking | Ktor-based HttpClient integration |
✅ Completed |
| 🧺 UI Testing | Compose UI + Navigation tests with custom TestApp |
✅ Completed |
| 🧲 DI for Testing | Custom TestApplicationComponent |
✅ Completed |
| ⚡ Coroutine Dispatchers | AppCoroutineDispatchers abstraction |
✅ Completed |
| 🩵 Logging | Kermit Logging via AppInitializers |
✅ Completed |
| 🛠 Environment Info | Dynamic ApplicationInfo (debug, flavor, version) |
✅ Completed |
| Feature | Priority | Notes |
|---|---|---|
| 🔐 Authentication Module | 🔜 Medium | Needs route factory, middleware, reducer setup |
| 💃 Room Database Integration | 🔜 Medium | For local caching, data source abstraction |
| 🌙 Dark Theme Support | 🔜 Low | Basic theming with system override |
| 📀 Pull-to-Refresh | 🔜 Low | Add to NewsListScreen for refresh API |
| 🩵 Paging | 🔜 Medium | Lazy load support for large news lists |
| 🥐 Unit Test Coverage | 🔜 High | Reducer, Middleware, UseCases |
| 📊 Analytics Module | 🔜 Low | Plug-in via middleware pipeline |
| 🔄 Multi-Module Structure | 🔜 High | Split :core, :features, :test-support, etc. |
- Test Harness: Custom
TestAppbootstrapped withTestApplicationComponent - Compose Rule: Uses
createAndroidComposeRule<MainActivity>() - DI Swapping: Inject Dispatchers and Dependencies to be changed
- Navigation Verification: End-to-end test across route boundaries
- Jetpack Compose
- Kotlin 1.9+
- Kotlin Inject (by @evant)
- Ktor for networking
- MVI (Middleware + Reducer pattern)
- Coroutine Dispatchers abstraction
- Kermit Logging
- JUnit + Compose UI Test
- Gradle Kotlin DSL
App
│
├── di/
│ ├── AndroidApplicationComponent
│ ├── AndroidActivityComponent
│ └── CommonAppComponent
│
├── features/
│ └── newsList/
│ ├── presentation/
│ │ ├── ui/ <-- Composables
│ │ ├── viewmodel/ <-- Reducer + Middleware
│ └── domain/
│
├── util/
│ └── AppCoroutineDispatchers.kt
│ └── ApplicationInfo.kt
│
└── e2e/
├── TestApp.kt
└── NewsListScreenE2ETest.kt
# Run the app
./gradlew installDebug
# Run UI tests
./gradlew connectedAndroidTest (make allTest)If you'd like to contribute or test-drive a feature like Authentication, Paging, or DB caching — feel free to fork and PR. Open to feedback and pairing on key improvements.
- 🔗 Heavily inspired by Tivi(https://github.com/chrisbanes/tivi)
- 🔗 Kotlin Inject Docs
- 🔗 Jetpack Compose Testing