Beginner14 min readDesign Patternslive prototype

MVC / MVP / MVVM

Three sibling patterns that all split an app into a *Model* (the data + rules), a *View* (what you see), and a *middle coordinator* that keeps them in sync — so your business logic never gets tangled up in buttons and pixels. Think of a **restaurant**: the kitchen (Model) cooks, the dining room (View) is what the guest sees, and a *waiter* stands between them. MVC, MVP, and MVVM just argue about *who the waiter is* and *which way the plates flow*.

The idea

What it is

Put a database call inside a button's click handler and your app rots fast: you can't test the logic without clicking, you can't reuse it on another screen, and one change ripples everywhere. MVC, MVP, and MVVM are three cures for the same disease. All three split your app into three parts: a Model (the data and the rules that guard it), a View (the buttons and pixels the user sees), and a middle coordinator that shuttles between them. The Model never touches the screen; the View never touches the database. That separation is the whole point — it's [[separation-of-concerns]] applied to UI.

Picture a restaurant. The kitchen is the Model — it holds the food and the recipes, and it doesn't care who's sitting at table 5. The dining room is the View — plates, menus, the stuff the guest actually sees. Between them stands a waiter who carries orders one way and dishes the other. The kitchen and the dining room never speak directly. That waiter is the middle coordinator — and MVC, MVP, and MVVM are simply three different waiters with three different sets of rules.

So what's actually different? Just who the middle guy is and which way data flows. In MVC the Controller takes your input and the View reads the Model itself. In MVP a Presenter does everything and hands finished results back to a totally passive View. In MVVM a ViewModel exposes state that the View is bound to, so a change on either side updates the other with no code you wrote. The trend across the three: the View gets progressively dumber and you write less and less manual wiring.

The one sentence to remember

All three separate a passive Model from a View via a middle coordinator; the only differences are who the coordinator is (Controller → Presenter → ViewModel) and how the View learns about changes — in MVC the View reads the Model, in MVP the Presenter pushes to the View, and in MVVM a two-way data binding syncs them automatically.

You've already used all three

Wrote a Rails, Django, or Spring controller? That's MVC. Built an old Android or WinForms screen with a Presenter and a passive view interface? That's MVP. Used Angular's [(ngModel)], Vue's v-model, WPF, SwiftUI, or Jetpack Compose? The framework handed you MVVM with data binding built in.

Mechanics

How it works

The three roles they all share

Before the differences, the common skeleton. Every one of the three has these three parts:

  • Model — the data plus the rules that protect it (a temperature, a user, a shopping cart, and the logic that says what's valid). It is passive about the UI: it never imports a button, never draws a pixel. It just holds truth.
  • View — everything the user sees and touches: labels, text fields, buttons. Its job is to display and to capture raw input. How much thinking it's allowed to do is exactly what separates the three patterns.
  • The middle coordinator — the piece that connects the two so they never touch directly. This is the Controller, the Presenter, or the ViewModel. Renaming this box and rewiring its arrows is the entire difference between MVC, MVP, and MVVM.
MVC MVP MVVM 📦 Model 🧭 Controller 🖼 View View reads Model 📦 Model 🧭 Presenter 🖼 View via interface 📦 Model 🧭 ViewModel 🖼 View ⇄ binding
Same three roles, three different wirings — the View gets dumber left to right.

MVC — the Controller takes input, the View reads the Model

Model-View-Controller is the classic, and the oldest (Smalltalk, 1979). The flow: the user acts on the View, the Controller receives that input, decides what it means, and updates the Model. The Model changes, and the View reads the Model to redraw itself. The key move is that the View is allowed to look at the Model directly — it pulls the data it needs. The Controller is mostly a router for input.

typescript
class TempModel { celsius = 20; }                 // data + rules

class TempController {
  constructor(private model: TempModel) {}
  onPlusClicked() { this.model.celsius += 1; }    // input → update Model
}

class TempView {
  constructor(private model: TempModel) {}
  render() { return `${this.model.celsius}°C`; }  // View READS the Model itself
}

This is what server frameworks give you — Rails, Django, Spring MVC, Laravel, ASP.NET MVC. A request hits a controller action, the action touches the model, and a template (the view) renders from that model. It fits the request/response web perfectly: each click is a fresh round trip.

MVP — the Presenter does everything, the View is passive

Model-View-Presenter makes the View as dumb as possible. The View captures a click and immediately forwards it: presenter.onPlusClicked(). It contains no logic — it doesn't read the Model, doesn't format anything, doesn't decide anything. The Presenter holds all of that. It updates the Model, formats the result, and pushes the finished value back to the View by calling methods on a small View interface like view.showTemperature("21°C").

typescript
interface TempView {                      // the ONLY door the Presenter uses
  showTemperature(text: string): void;
}

class TempPresenter {
  constructor(private model: TempModel, private view: TempView) {}
  onPlusClicked() {
    this.model.celsius += 1;               // update the Model
    this.view.showTemperature(`${this.model.celsius}°C`); // PUSH result to View
  }
}

Why MVP is the testable one

Because the View is just an interface the Presenter talks to, you can write a fake View in a unit test — call presenter.onPlusClicked() and assert the fake received "21°C". No screen, no framework, no clicking. That's why classic Android (pre-Jetpack) and WinForms teams loved MVP.

MVVM — data binding syncs the View and ViewModel for you

Model-View-ViewModel removes the manual push entirely. The ViewModel exposes bindable state — an observable temperatureText property. The View declares, once, that its label is bound to that property. From then on, two-way data binding keeps them in sync automatically: change the ViewModel and the label updates itself; type in a bound text field and the ViewModel updates itself. You write no glue code to copy values back and forth — the framework does it.

typescript
class TempViewModel {
  private model = new TempModel();
  temperatureText = observable(`${this.model.celsius}°C`); // bindable state

  increment() {
    this.model.celsius += 1;
    this.temperatureText.set(`${this.model.celsius}°C`);   // binding auto-updates the View
  }
}
// In the view template:  <label text="{bind temperatureText}" />  ← no push code

This is the modern default because frameworks ship the binding engine: WPF/XAML (where MVVM was coined), Angular, Vue, Knockout, SwiftUI, and Jetpack Compose. You describe what the UI should reflect, not when to update it.

The through-line: the View keeps getting dumber

MVC → MVP → MVVM is one story told three times, each time moving more work out of the View. In MVC the View still reads the Model. In MVP the View knows nothing and is fed by the Presenter. In MVVM the View doesn't even get fed — it's bound, and updates happen with no wiring you wrote. Less manual glue at every step.

A quick side-by-side

  • Who's the middleman? MVC → Controller · MVP → Presenter · MVVM → ViewModel.
  • How does the View get updated? MVC → it reads the Model · MVP → the Presenter pushes to it via an interface · MVVM → data binding updates it automatically.
  • How smart is the View? MVC → medium (reads Model, some logic) · MVP → dumb (pure interface) · MVVM → declarative (just bindings).
  • Coupling of View↔coordinator? MVC → Controller doesn't reference the View much · MVP → Presenter holds a View interface reference · MVVM → ViewModel doesn't even know the View exists (it just exposes observable state).
  • Where you meet it: MVC → Rails, Django, Spring · MVP → old Android, WinForms · MVVM → WPF, Angular, Vue, SwiftUI, Compose.

Interactive prototype

See it. Build it. Break it.

A sandboxed, hands-on simulation — no setup, no install. Play with it as you read.

About this simulation

A tiny temperature app — a 📦 Model holding one number in °C, a 🖼 View that shows it, and a +1°C button. Flip between the MVC, MVP, and MVVM tabs, then press +1°C and watch which arrows light up. In MVC the View pokes the Controller, the Controller writes the Model, and the View reads the Model back. In MVP the View is dumb — it only ever talks through the Presenter, which pushes the finished text back to it. In MVVM one glowing ⇄ binding arrow does the work both ways automatically — notice the "glue you wrote" counter stays at 0. Same app, same number; the only thing that changes is who carries the plates.

Hands-on

Try these yourself

Open the prototype above, predict what happens, then verify.

try 01

Trace the MVC round trip

Make sure the MVC tab is selected. Press +1°C in the View box. Watch the arrows light up in order: View → Controller (you told the controller), Controller → Model (it bumped the number), then Model → View (the View re-reads the Model to redraw). The shared temperature ticks up by one. Notice the explain line: the View had to reach back to the Model itself.

try 02

See the passive View in MVP

Switch to the MVP tab and press +1°C. Now the flow is View → Presenter (the View just forwards the click), Presenter → Model (update), then Presenter → View (the Presenter pushes the finished '°C' text back through the view interface). The View never touched the Model — it only ever spoke to the Presenter. Watch the 'glue you wrote' counter tick up: in MVP you hand-wire every push.

try 03

Watch binding do the work in MVVM

Switch to the MVVM tab and press +1°C. One glowing ⇄ arrow between View and ViewModel pulses both directions — that's two-way data binding. The ViewModel updates the Model, and the bound label refreshes with zero push code. Check the 'glue you wrote' counter: it stays at 0. Hit ↺ Reset any time to set the temperature back to 20°C and clear the counters.

In practice

When to use it — and what trips people up

Reach for...

  • MVC — when you're building a server-rendered, request/response app. Each click is a round trip: controller handles the request, touches the model, renders a template. Rails, Django, Spring, Laravel, and ASP.NET MVC are all built around it.
  • MVP — when the View must be dumb and unit-tested. If you need to verify presentation logic without spinning up a screen (a fake view interface in a test), MVP's passive View is exactly the shape you want. Classic for legacy Android and desktop WinForms/GWT.
  • MVVM — when your framework gives you data binding. If you're in WPF, Angular, Vue, SwiftUI, or Jetpack Compose, lean into the ViewModel and let bindings erase the glue code. Fighting the binding engine to do MVP by hand is wasted effort.

Which to pick / skip when...

  • The app is tiny — a one-screen toy doesn't need any of them. A Model plus a click handler is fine; adding a Presenter is ceremony with no payoff. Reach for a pattern when logic grows.
  • No binding engine? Don't fake MVVM. Two-way binding by hand is fiddly and leaky. Without framework support, MVP (explicit pushes) is usually cleaner and easier to follow than a homegrown binding layer.
  • Don't mix the roles. The moment SQL or business rules leak into the View, or the Model starts importing UI widgets, you've lost the [[separation-of-concerns]] that made you pick a pattern at all.

What it gives you

  • Separation of concerns — the Model (data + rules) stays free of UI, so business logic is reusable across screens and independently testable.
  • Testability, especially in MVP and MVVM — the coordinator holds the logic and can be tested with a fake or headless View, no clicking required.
  • Parallel work — once the interfaces are set, one person can build the View while another builds the Model and coordinator.
  • Less glue over time — MVVM's data binding erases the manual copy-values-back-and-forth code that MVC and MVP require.

Common mistakes

  • Overhead for small apps — three roles plus interfaces is real boilerplate that a trivial screen doesn't earn back.
  • MVP can get chatty — the Presenter needs a method on the View interface for every little UI update, which balloons on complex screens.
  • MVVM binding is hard to debug — 'why did this update?' hides inside the framework's binding engine, and leaky bindings can cause subtle bugs and memory leaks.
  • Blurry boundaries in practice — teams argue endlessly over what belongs in the Controller vs Model vs View, and the lines drift as the app grows.

Reference

Code & further reading

A minimal reference implementation and pointers worth bookmarking.

// ---- MVC: Controller takes input, View reads the Model ----

class TempModel {
  celsius = 20;                              // data + the rule that guards it
  bump(delta: number) { this.celsius += delta; }
}

class TempController {                        // the middleman: routes input
  constructor(private model: TempModel) {}
  onPlus() { this.model.bump(1); }           // input → update Model
}

class TempView {
  constructor(private model: TempModel, private ctrl: TempController) {}
  clickPlus() { this.ctrl.onPlus(); }        // View tells the Controller
  render(): string {
    return `${this.model.celsius}°C`;        // View READS the Model directly
  }
}

const model = new TempModel();
const ctrl = new TempController(model);
const view = new TempView(model, ctrl);

view.clickPlus();
console.log(view.render());                  // "21°C"

References & further reading

6 sources

Knowledge check

Did it land?

Quick questions, answers revealed on submit. Sign in to save your best score.

question 01 / 06

What do MVC, MVP, and MVVM all have in common?

question 02 / 06

Which of the three patterns uses two-way data binding to sync the View and the coordinator automatically?

question 03 / 06

In MVP, who does the View talk to, and how much logic does the View contain?

question 04 / 06

In classic MVC, how does the View learn about a change after the Controller updates the Model?

question 05 / 06

What is the overall trend as you move from MVC to MVP to MVVM?

question 06 / 06

You're building a server-rendered app where each user click is an HTTP request that returns a rendered page. Which pattern fits most naturally?

0/6 answered