Why Architecture Isn't About Boring Diagrams
Imagine you're building a house. You could just pile up bricks, pour concrete, and hope the structure doesn't collapse. Or you could design the foundation, load-bearing walls, and floors. The same goes for JavaScript development: without a well-thought-out architecture, your project turns into "spaghetti code" where fixing one function breaks the entire application.
Architectural patterns are not dry theory from books. They are battle-tested blueprints that will help you:
- Keep your sanity when the project grows to 50,000 lines of code.
- Find bugs quickly because the code is divided into logical blocks.
- Add features without fear of breaking something.
In this article, we'll cover five key patterns for JavaScript applications: from the classic MVC to the Module approach. Let's go!
MVC (Model-View-Controller): The Grandfather of All Patterns
MVC is the gold standard of web development. The pattern divides the application into three layers:
- Model — data and business logic (CRUD, validation).
- View — the interface the user sees (HTML, rendering).
- Controller — the mediator: receives user requests and updates the Model or View.
Here's what it looks like in code:
// Modelclass UserModel { constructor() { this.users = []; }
addUser(user) { this.users.push(user); }
getUsers() { return this.users; }}
// Viewclass UserView { render(users) { const list = document.getElementById('user-list'); list.innerHTML = users.map(u => `<li>${u.name}</li>`).join(''); }}
// Controllerclass UserController { constructor(model, view) { this.model = model; this.view = view; }
addUser(name) { this.model.addUser({ name, id: Date.now() }); this.view.render(this.model.getUsers()); }}
// Usageconst model = new UserModel();const view = new UserView();const controller = new UserController(model, view);controller.addUser('Alice');controller.addUser('Bob');When to use MVC in JavaScript? Ideal for medium and large SPAs (single-page applications). But be careful: if the View is heavily dependent on the Model, the pattern turns into "Massive View Controller" — the controller becomes bloated beyond decency.
MVP (Model-View-Presenter): The Strict Overseer
MVP is an evolution of MVC, where the Presenter takes over all the logic of managing the View. Unlike MVC, the View in MVP is a passive "dumb" layer. It only displays data and passes events to the Presenter.
// Model (same as in MVC)class UserModel { constructor() { this.users = []; } addUser(user) { this.users.push(user); } getUsers() { return this.users; }}
// View — passive interfaceclass UserView { constructor() { this.list = document.getElementById('user-list'); this.addButton = document.getElementById('add-btn'); }
bindAddUser(handler) { this.addButton.addEventListener('click', () => { const name = prompt('Enter name:'); if (name) handler(name); }); }
render(users) { this.list.innerHTML = users.map(u => `<li>${u.name}</li>`).join(''); }}
// Presenter — the brain of the applicationclass UserPresenter { constructor(model, view) { this.model = model; this.view = view; this.view.bindAddUser(this.handleAddUser.bind(this)); this.view.render(this.model.getUsers()); }
handleAddUser(name) { this.model.addUser({ name, id: Date.now() }); this.view.render(this.model.getUsers());