MVC, MVP, MVVM#
Three patterns that all separate what the user sees (View) from what the app does (Model). The difference is who orchestrates - controller, presenter, or viewmodel.
flowchart LR
subgraph MVC
V1[View] -->|user action| C1[Controller] --> M1[Model]
M1 -->|state| V1
end
subgraph MVP
V2[View] -->|delegates| P[Presenter] --> M2[Model]
P -->|updates| V2
end
subgraph MVVM
V3[View] ---|two-way binding| VM[ViewModel] --> M3[Model]
end
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
class V1,V2,V3 edge;
class C1,P,VM,M1,M2,M3 service;
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
MVC dominates server-rendered web; MVP suits mockable desktop UIs; MVVM powers two-way data binding in WPF, Android, SwiftUI, Vue, and React + state libraries.
The presentation triad. All three split UI from logic; they differ in who owns rendering, who handles input, and who knows the most.
MVC - Model-View-Controller#
Coined by Trygve Reenskaug at Xerox PARC (1979). The original Smalltalk variant is not quite what we call MVC today.
sequenceDiagram
participant U as User
participant V as View
participant C as Controller
participant M as Model
U->>V: click submit
V->>C: dispatch action
C->>M: update state
M-->>V: emit change
V-->>U: re-render
- Model: the data and rules.
- View: rendering of Model.
- Controller: translates user input into Model changes.
Server-side MVC#
Rails, Django, Spring MVC: a request hits a Controller method, which calls Model methods, then picks a View template:
class PostsController < ApplicationController
def show
@post = Post.find(params[:id]) # model
render :show # view
end
end
The View is rendered server-side; the round-trip is the entire interaction.
MVP - Model-View-Presenter#
Used in WPF (early), GWT, Android (pre-MVVM).
classDiagram
class View {
<<interface>>
+showLoading()
+showOrders(list)
+showError(msg)
}
class Presenter {
-view: View
-repo: OrderRepo
+loadOrders()
}
class Model
Presenter --> View
Presenter --> Model
- View: passive; just renders what it's told. Defined as an interface.
- Presenter: holds all logic. Talks to Model, formats results, calls View methods.
- Model: domain data.
Because the View is an interface, the Presenter is fully unit-testable with a mock View. This is MVP's main draw over MVC.
MVVM - Model-View-ViewModel#
Microsoft (WPF, 2005), Android (Architecture Components 2017+), SwiftUI, Vue.
flowchart TB
V[View<br/>declarative template] ---|binding| VM[ViewModel<br/>observable state + commands]
VM --> M[Model]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
- ViewModel: exposes Model data as observable properties + commands.
- View: binds to ViewModel properties; updates fire automatically.
- Data binding is the magic that removes most controller-style boilerplate.
// Android Jetpack
class OrderViewModel(repo: OrderRepo) : ViewModel() {
val orders: LiveData<List<Order>> = liveData {
emit(repo.fetchOrders())
}
fun cancel(id: String) = viewModelScope.launch {
repo.cancel(id)
}
}
@Composable
fun OrderScreen(vm: OrderViewModel = viewModel()) {
val orders by vm.orders.observeAsState(emptyList())
LazyColumn { items(orders) { OrderRow(it, onCancel = vm::cancel) } }
}
React and friends#
React's component model is closest to MVVM with one-way binding:
- State is the ViewModel.
- JSX render is the View bound to that state.
- Updates flow state → render; events flow back via handlers.
State management libraries (Redux, MobX, Zustand) push the ViewModel out of components entirely.
Side-by-side#
| MVC | MVP | MVVM | |
|---|---|---|---|
| Orchestrator | Controller | Presenter | (binding) |
| View knowledge | Some | None (passive) | None |
| Testability | Medium | High | High |
| Binding required | No | No | Yes |
| Best fit | Server-rendered web | Desktop, mockable UIs | Declarative UI frameworks |
Modern hybrids#
- MVI / Redux: state is reduced from actions; view is a pure function of state.
- Flux: unidirectional MVC (action → dispatcher → store → view).
- MVVM-C: add a Coordinator that owns navigation; useful in iOS.
Common mistakes#
- Putting business logic in the Controller / Presenter / ViewModel. It belongs in the Model.
- Letting the View call services directly - couples rendering to side effects.
- Massive ViewModels - split per screen, not per app.
Where the presentation patterns sit#
flowchart TB
MV((MVC / MVP /<br/>MVVM))
OOP[OOP pillars<br/>polymorphism in View iface]
CA[Clean architecture<br/>outer layer]
SM[State machines<br/>view state modelling]
BPL[Behavioral patterns<br/>Observer underlies binding]
OOP -. backs .- MV
CA --> MV
MV --> SM
BPL -. underlies .- MV
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class MV service;
class OOP,CA,SM,BPL datastore;
Glossary & fundamentals#
| Tag | Concept | What it is | Page |
|---|---|---|---|
LLD |
OOP pillars | encapsulation + polymorphism in the View interface | oop-pillars |
LLD |
Clean architecture | the layer above which MVC/MVP/MVVM lives | clean-architecture |
LLD |
State machines | view state often modelled as a state machine | state-machines |
LLD |
Behavioral patterns | Observer pattern underlies data binding | behavioral-patterns |
Quick reference#
At a glance#
| Pattern | Orchestrator | View | Binding |
|---|---|---|---|
| MVC | Controller | Active | No |
| MVP | Presenter | Passive (interface) | No |
| MVVM | None (data binding) | Bound | Yes |
Where each lives#
- MVC: Rails, Django, Spring MVC; server-rendered web
- MVP: Android pre-Jetpack, GWT, mockable UIs
- MVVM: WPF, Android Jetpack, SwiftUI, Vue, React with state libs
Modern hybrids#
- React = MVVM with one-way binding
- Redux / MVI = state reduced from actions; view = f(state)
- MVVM-C = adds Coordinator for navigation
Rules of thumb#
- Business logic in Model, not in Controller/Presenter/ViewModel
- View calls no services directly
- ViewModel scoped per screen, not per app
Common bugs#
- Massive controller / presenter / viewmodel
- Two-way binding loops
- View knowing about ORM entities
Refs#
- Reenskaug - original MVC paper
- Fowler - GUI Architectures
- Android Architecture docs
FAQ#
What is the difference between MVC, MVP, and MVVM?#
All three separate the View from the Model. MVC uses a controller, MVP a presenter that owns view updates, and MVVM a viewmodel bound to the view via two-way data binding.
When should I use MVVM over MVC?#
MVVM shines when your UI framework supports data binding such as WPF, SwiftUI, Android Jetpack, or Vue. MVC fits stateless server-rendered web apps better.
Is MVC outdated?#
No. Server-side MVC is still the dominant pattern for Rails, ASP.NET, Spring MVC, Laravel, and Django. The shape is timeless even if names vary.
What is a presenter in MVP?#
A presenter owns the view's state and tells the view what to display. The view is a passive interface, which makes presenter logic easy to unit test.
Can MVC, MVP, and MVVM coexist?#
Yes. Large apps often use MVC at the routing level and MVVM inside specific screens. Pick the pattern that matches each layer's testing and binding needs.
Related Topics#
- Clean Architecture: MVC/MVP/MVVM are presentation-layer patterns inside a clean architecture
- Behavioral Patterns: the Observer pattern underlies every data-binding implementation
- State Machines: view state and navigation are often best modelled as state machines