MVVM is useful when it clarifies responsibilities: the UI renders immutable state, the ViewModel coordinates user intents, and repositories own data access. It is not a reason to create a class for every operation.
Practical guidance
- Expose one coherent screen state from the ViewModel.
- Keep Android UI types out of repositories and domain rules.
- Make the repository the boundary between local and remote sources.
- Test state transitions instead of private implementation details.
Working example
data class UsersState(val loading: Boolean = true, val users: List<User> = emptyList())
class UsersViewModel(private val repo: UserRepository) : ViewModel() {
val state = repo.observeUsers()
.map { UsersState(loading = false, users = it) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), UsersState())
}
Common mistakes
- A ViewModel that only forwards every repository method adds little value.
- Multiple unrelated observable fields permit impossible combinations.
- Passing Context into business logic makes testing and reuse harder.
Key takeaway
Organize code around data ownership and state transitions. MVVM succeeds when the UI has a predictable input and a narrow set of intents.
