App Architecture: MVVM, MVI & Clean Architecture
Why architecture is the foundation
Architecture decides how responsibilities are split in your app. Get it right and features are easy to add, bugs are easy to find, and the app survives rotation and process death. Get it wrong (everything in one giant Activity/ViewController) and the app becomes a fragile mess. Architecture is the backbone of mobile system design.
The layered idea
Almost every good mobile architecture splits the app into three broad layers:
- Presentation — the UI and the state holder (ViewModel). Shows state, sends events.
- Domain — the business rules and use cases, independent of any framework.
- Data — repositories that fetch/store data from network and database.
The golden rule: dependencies point inward. The UI depends on the domain; the domain depends on nothing. This keeps business logic pure and testable.
MVVM (Model–View–ViewModel)
The most common pattern. The View (Activity/Fragment/Compose/SwiftUI) observes a ViewModel that holds UI state and survives rotation; the ViewModel talks to repositories (the Model).
class FeedViewModel(private val repo: FeedRepository) : ViewModel() {
val state: StateFlow<FeedUiState> = ... // UI observes this
fun refresh() { /* call repo, update state */ }
}
MVI (Model–View–Intent)
A stricter evolution of MVVM with unidirectional data flow: the UI emits intents (events), which produce a single immutable state object that the UI renders. Predictable and great for complex screens.
// one state object describes the whole screen
data class FeedState(val loading: Boolean, val posts: List<Post>, val error: String?)
// UI sends intents -> reducer produces a new state -> UI renders it
Clean Architecture
A way of organising the three layers with strict boundaries and use cases (single-purpose business operations like GetFeedUseCase). It maximises testability and lets you swap implementations (e.g. change the database) without touching business logic. It can be overkill for small apps but shines on large ones.
The crucial cross-cutting concern: state survival
On mobile, the OS destroys and recreates screens (rotation) and can kill the whole process in the background. A good architecture keeps important state in a ViewModel (survives rotation) and persists critical data to disk (survives process death) — never only in a view.
Choosing an architecture
- Small app/feature — MVVM is plenty.
- Complex, stateful screens — MVI for predictable state.
- Large, long-lived app with many devs — Clean Architecture with use cases.
The point isn’t the buzzword — it’s consistent layering, unidirectional data flow, and testable business logic.
Common mistakes
- Putting logic and networking directly in the UI (Activity/ViewController).
- Over-engineering a tiny app with full Clean Architecture.
- Keeping important state only in the view, losing it on rotation/process death.
Summary: Split the app into Presentation, Domain and Data layers with dependencies pointing inward. Use MVVM by default, MVI for complex state, and Clean Architecture for large apps. Keep state in ViewModels and persist critical data so it survives rotation and process death.