MVVM (Google Recommended) vs MVI

1. Input Processing (Events)
MVVM (Direct Method Calls)
UI triggers dedicated ViewModel methods directly.
Button(onClick = { viewModel.onSubmitClicked() })
MVI (Single Entry Point)
All UI actions are wrapped into a single Intent sealed interface and sent to one handler.
Button(onClick = { viewModel.onIntent(UserIntent.Submit) })
2. State Reduction (State Updates)
MVVM (Direct State Mutation)
State is updated directly inside asynchronous calls via _uiState.update { ... }.
fun loadData() {
viewModelScope.launch {
val result = repository.getData()
_uiState.update { it.copy(data = result) }
}
}
MVI (Pure Reducer Function)
The next state is calculated via a pure reduce function based on the current state and the received action.
fun reduce(oldState: UiState, action: Action): UiState = when (action) {
is Action.DataLoaded -> oldState.copy(data = action.data)
is Action.Loading -> oldState.copy(isLoading = true)
}
3. Output Processing (UI Updates & Side Effects)
MVVM (State-Driven Single Flow)
Both persistent state and one-off events are encapsulated inside a single UiState. Single-shot events are consumed and cleared by the UI.
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
val state by viewModel.uiState.collectAsStateWithLifecycle()
LaunchedEffect(state.userMessage) {
state.userMessage?.let {
snackbarHostState.showSnackbar(it)
viewModel.onMessageShown() // Consume event
}
}
MVI (Separated Side Effects Flow)
State rendering and single-shot events are decoupled. Side effects stream through a Channel or SharedFlow.
private val _effect = Channel<UiEffect>()
val effect = _effect.receiveAsFlow()
val state by viewModel.state.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
viewModel.effect.collect { effect ->
when (effect) {
is UiEffect.ShowSnackbar -> snackbarHostState.showSnackbar(effect.message)
}
}
}
4. Architecture Selection Guide
| Criteria | Google Recommended MVVM | Pure MVI |
|---|---|---|
| Primary Goal | Pragmatic developer velocity | Strict state predictability |
| Boilerplate | Low to Moderate | High (Sealed classes for Intent, Action, Effect) |
| State Mutation | Scattered across ViewModel methods | Centralized inside Reducer |
| Ideal Team Size | Any team size | Mid to Large teams requiring strict patterns |
| Debugging | Standard break-pointing | Easy event logging & state snapshot inspection |