Suggest an editImprove this articleRefine the answer for “What is MVC architecture pattern?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**MVC (Model-View-Controller)** is an architecture pattern that splits an app into three parts: Model (data and business logic), View (UI), and Controller (handles input, connects the other two). ```javascript // User clicks a button UserController.load(1); // Controller asks Model, gets user, passes to View // View renders: <h1>Alice</h1> ``` **Key point:** Controller mediates all communication. View never talks to Model directly.Shown above the full answer for quick recall.Answer (EN)Image**MVC (Model-View-Controller)** is an architecture pattern that splits an application into three components: Model handles data and business logic, View handles the UI, and Controller sits between them handling user input. ## Theory ### TL;DR - Model = data layer (queries, validation, state); View = what the user sees; Controller = the coordinator between the two - Analogy: a restaurant. The kitchen (Model) manages food. The plate and presentation (View) is what reaches the customer. The waiter (Controller) takes orders and connects them. - Core flow: user acts in View, Controller updates Model, Model notifies View - Use MVC when UI logic and data logic need to grow independently - Skip it for scripts under ~500 lines or pure API services with no View layer ### Quick Example ```javascript // Model: data logic only const UserModel = { users: [{ id: 1, name: 'Alice' }], getUser(id) { return this.users.find(u => u.id === id); } }; // View: rendering only const UserView = { render(user) { document.getElementById('app').innerHTML = `<h1>${user.name}</h1>`; } }; // Controller: handles input, connects the other two const UserController = { load(id) { const user = UserModel.getUser(id); UserView.render(user); // Output: <h1>Alice</h1> } }; UserController.load(1); ``` Each piece has one job. Model does not know how the UI looks. View does not know where data comes from. Controller knows about both, but only to connect them. ### How the Three Components Actually Connect Model holds state and exposes methods to read or modify it. It knows nothing about views or controllers. View receives data and renders it. It has no idea where that data came from. Controller listens for user events, calls Model methods, then tells View what to display. The direction is always the same: user action hits Controller, Controller talks to Model, data comes back, Controller passes it to View. No shortcuts. View never calls Model directly. This one-way discipline is what separates MVC from older patterns where UI and data were tangled together. Once you skip that rule, components start fetching their own data, triggering side effects on render, and breaking every time the data layer changes. ### When to Use MVC - Web app with a real UI and backend data: MVC (Rails, ASP.NET MVC, Spring MVC) - Express.js API with templates: fits cleanly (routes act as Controllers, EJS or Handlebars are Views) - React SPA with complex state: consider Flux or Redux instead (MVC gets awkward with bidirectional React state) - CLI tool or small script: skip the pattern, plain functions work fine - Team of 5+ developers: MVC lets people work on separate layers in parallel without stepping on each other - Microservice with no UI: Model and Controller only, no View layer needed ### Comparison: MVC vs Monolith vs Flux | Aspect | MVC | Monolith | Flux/Redux | |--------|-----|----------|------------| | Data flow | Controller to Model to View | Often bidirectional | Strictly unidirectional | | Testing | Isolate layers independently | Requires full app mocks | Unit test actions and reducers | | Parallel dev | Teams work on separate layers | Single dev bottleneck | Global state, harder to split | | Best for | Traditional web apps (Rails, ASP.NET) | Prototypes under 1k LOC | React apps with complex client state | ### How It Works at Runtime In a browser, a click event reaches the Controller first via `addEventListener`. The Controller calls a Model method, which may run an async database query through `fetch` or an ORM like Sequelize. Once data is ready, the Model can emit a custom event or use a pub/sub mechanism. The View listens and re-renders. In frameworks like Rails, this happens at the HTTP level: a request maps to a Controller action, which calls Model methods, and the result gets passed to a View template (ERB, Handlebars, Thymeleaf). Same pattern, just over HTTP instead of DOM events. ### Common Mistakes **1. View fetching data directly** ```javascript // Bad: View is tightly coupled to Model function UserView() { const user = UserModel.getUser(1); // breaks when Model goes async return `<div>${user.name}</div>`; } // Good: Controller mediates UserController.load(1); // Controller calls Model, passes result to View ``` View fetching its own data seems convenient until the data source changes. Then you are editing every View instead of one Model method. **2. Fat Controller** The Controller ends up doing database queries, validation, business logic, and rendering. 500+ lines, impossible to test. The fix: Controller only calls Model methods and View methods. All logic stays in Model. **3. Stale View after Model update** ```javascript // Bad: Model updates but View never knows Model.updateUser(id, data); View.render(null); // stale data shown to user // Good: Model emits event, View re-renders Model.updateUser(id, data); // triggers 'change' event model.addEventListener('change', (e) => View.render(e.detail)); ``` This is the most common "MVC View not updating" issue on Stack Overflow. The Model update happened but nobody told the View. **4. Synchronous Model in Node.js** Blocking the event loop with sync database calls hangs the entire server. Always use `async/await` for Model methods. The Controller catches errors from rejected Promises and passes an error state to the View. ### Real-World Usage - Ruby on Rails 7: `app/models/user.rb`, `app/views/users/show.html.erb`, `app/controllers/users_controller.rb` - ASP.NET Core 8: `Models/User.cs`, `Views/User/Index.cshtml`, `Controllers/UserController.cs` - Spring MVC (Java): `@Service` for Model logic, Thymeleaf for View, `@Controller` for routing - Backbone.js 1.4: built around MVC with `model.bind()` connecting directly to `view.render()` - Django uses a variant called MVT (Model-View-Template) where the "View" behaves closer to a Controller ### Follow-up Questions **Q:** Draw the MVC flow for a user submitting an edit form. **A:** User fills in and submits the form (View). Controller catches `onSubmit`, validates input, calls `Model.save()`. Model persists the data and emits a `change` event. Controller receives the event and tells View to render a success message. **Q:** How do you test each MVC layer independently? **A:** For Model tests, use a test database or mock the ORM. For Controller tests, stub the Model with sinon.js and verify that the correct View methods get called. For View tests, pass a data fixture directly and snapshot the output. **Q:** What is the difference between MVC and MVVM? **A:** In MVVM (Vue.js, Angular), the ViewModel binds directly to the View two-way. Changes in View automatically update ViewModel and vice versa. In MVC, the Controller is not bound to View data at all. It passes data through explicitly. **Q:** How do you handle Model errors in the View? **A:** Controller wraps the Model call in try/catch, or handles Promise rejection, then calls `View.renderError(error)` instead of `View.render(data)`. View gets an error state object, not a raw exception. **Q:** How would you scale MVC across micro-frontends? **A:** Each micro-frontend gets its own Controller and View. Shared Model state flows through an event bus (RxJS Subject or a custom EventEmitter). Shadow DOM isolates View styles and events so micro-apps do not bleed into each other. **Q:** With React hooks available, does MVC still apply in 2024? **A:** Hooks are Controller logic extracted into reusable functions. `useEffect` handles side effects the same way a Controller would. Next.js App Router closely mirrors MVC: server components act as Model and Controller, client components act as View. The pattern is alive, just not always labeled. ## Examples ### Basic MVC: Loading a User in Vanilla JS ```javascript // Model const UserModel = { users: [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' } ], getUser(id) { return this.users.find(u => u.id === id) || null; } }; // View const UserView = { render(user) { const app = document.getElementById('app'); if (!user) { app.innerHTML = '<p>User not found</p>'; return; } app.innerHTML = `<h1>${user.name}</h1><p>${user.email}</p>`; } }; // Controller const UserController = { init() { document.getElementById('load-btn').addEventListener('click', () => { const user = UserModel.getUser(1); UserView.render(user); // Output: Alice / alice@example.com }); } }; UserController.init(); ``` Model has no idea there is a button or a DOM node. View has no idea where users come from. That is the whole point of the separation. ### Intermediate: Express.js REST Controller ```javascript const express = require('express'); const Database = require('better-sqlite3'); const db = new Database('app.db'); const app = express(); app.use(express.json()); // Model: database access only const UserModel = { getUser(id) { return db.prepare('SELECT * FROM users WHERE id = ?').get(id); }, updateUser(id, data) { db.prepare('UPDATE users SET name = ? WHERE id = ?').run(data.name, id); return this.getUser(id); } }; // Controller: Express route handlers act as the Controller layer app.get('/users/:id', (req, res) => { const user = UserModel.getUser(req.params.id); if (!user) return res.status(404).json({ error: 'Not found' }); res.json(user); // Output: { id: 1, name: 'Alice' } }); app.put('/users/:id', (req, res) => { const updated = UserModel.updateUser(req.params.id, req.body); res.json(updated); // Output: { id: 1, name: 'Bob' } }); // View: the JSON response is the View in a REST API ``` In a REST API the View is the JSON response. The separation still holds: Model talks to the database, Controller handles request and response, View is assembled from the data the Controller provides. ### Advanced: Model with Pub/Sub, View Subscribes Directly ```javascript // Model with event notification (no knowledge of who listens) class UserModel extends EventTarget { constructor() { super(); this.data = { id: 1, name: 'Alice' }; } update(name) { this.data.name = name; this.dispatchEvent(new CustomEvent('change', { detail: { ...this.data } })); } } const model = new UserModel(); // View subscribes to Model events (wired up by Controller on init) model.addEventListener('change', (e) => { document.getElementById('username').textContent = e.detail.name; // Output: DOM updates instantly without full page re-render }); // Controller document.getElementById('save-btn').addEventListener('click', () => { const newName = document.getElementById('name-input').value; model.update(newName); // fires 'change' event above }); ``` One thing I noticed building this pattern in production: View subscribing to Model events is clean in vanilla JS, but in React 18+ it bypasses the reconciler entirely. Wrap Model-driven updates in `useEffect`, or use a state manager like Zustand, to stay inside React's rendering cycle.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.