← wiki index08 / 10 · The learning path

🏛️ The learning path

MVVM Architecture

Unidirectional data flow in a UI — why the View never talks to the data layer, and what the ViewModel actually is.

6 min read5 key concepts

MVVM — Model-View-ViewModel — is a way of structuring a screen so that the rules of the UI are testable without a device, and the View is reduced to rendering. It's one of a family of "the View is dumb, the logic lives somewhere else" patterns (MVP, MVI, MVVM) that all solve the same problem: if the interesting logic lives inside an Activity or Fragment, it can't be tested except on an emulator, and it entangles business rules with Android framework code.

The unidirectional data flow rule

The core discipline of MVVM is one direction of travel. The View (Activity/Fragment/Composable) observes state and dispatches events; it never reaches past the ViewModel to touch the data layer. The ViewModel holds all screen state and translates events into changes to that state. The data layer (repositories, network, database) is another step removed, behind the ViewModel.

  • View → ViewModel: user intent, expressed as method calls or events ("user tapped refresh")
  • ViewModel → View: immutable state, exposed as a `StateFlow<UiState>` (or LiveData in older code) the View collects
  • ViewModel → Model: calls into a repository, which hides where data actually comes from
  • The View never constructs or calls the Model directly — that single rule is what keeps the layers from collapsing into a ball of mud

What the ViewModel actually is

Despite the name, a ViewModel is not a model and it isn't a view. It's a screen's state holder: it owns the screen's state, survives configuration changes (like rotation) because the framework holds it across Activity recreation, and dies when the screen is truly finished. Its real value is that it has no Android View dependency — it can be unit-tested on the JVM with fake data sources, which is why "move logic to the ViewModel" is the most profitable refactor in Android.

  • Survives rotation — the ViewModel is scoped to the ViewModelStore, which outlives the Activity, so a rotated screen re-binds to the same state instead of refetching it
  • No View reference — it never holds a Context or View, only state and dependencies, which is what makes it testable
  • Exposes a single `UiState` — a sealed class (`Loading` / `Success` / `Error`) so the UI can't render a state the data can't actually be in
  • Runs work in a coroutine scope (`viewModelScope`) that's auto-cancelled when the ViewModel is cleared, so no orphaned work updates a dead screen

Where dependency injection fits

MVVM says the ViewModel needs a repository; it doesn't say how it gets one. Dependency injection (Dagger, Hilt, Koin, or manual wiring) is what constructs the object graph — the ViewModel is given its repository rather than instantiating it. This is not decorative: a ViewModel that constructs its own repository can't be given a fake in a test, which guts the reason MVVM exists. DI and MVVM are a matched pair.

MVVM vs MVI vs MVP, in one breath

  • MVP — the View exposes an interface the Presenter drives; the View is passive but the interface tends to grow huge
  • MVVM — the View observes state; the ViewModel exposes it; two-way data binding optional
  • MVI — a stricter MVVM: a single immutable state, and events reduced through a pure function, so state changes are a deterministic `(state, event) -> state` — the most testable, and the most ceremony
  • They're points on a spectrum of "how much of the screen is a pure function"; MVVM is the pragmatic middle that most production Android lands on

In this note

ViewModelUiStaterepositoryunidirectional flowdependency injection

Prefer it hands-on?

This note has a matching interactive topic with diagrams and a runnable repo.