The MVC Pattern
Model-View-Controller (MVC) is the architectural pattern underlying most web frameworks — Rails, Django, Laravel, and in spirit, Express. It separates an application into three collaborating roles: the Model (data and business logic), the View (presentation, often a template or JSON response), and the Controller (orchestrates: handles the request, asks the model for data, gives it to the view). For API-only Node backends the "view" is usually just JSON serialization rather than HTML templates, but the separation is identical. Understanding MVC clarifies why the layered architecture looks the way it does.
The three roles
Role | Responsibility | In a Node API |
|---|---|---|
Model | Data structure, business rules, persistence, validation | Service + repository + type/schema definitions |
View | Presentation — renders data for the user | JSON serialization, response formatting (or a template engine for SSR) |
Controller | Receives input, coordinates model & view | Express route handler — parses req, calls service, sends res |
MVC in Express
// MODEL — data shape + business logic (service + repo)
// users.service.ts
export class UsersService {
async getById(id: string): Promise<User> {
const user = await this.repo.findById(id)
if (!user) throw new NotFoundError(`User ${id} not found`)
return user
}
}
// VIEW — response serialization (often inline, sometimes a presenter/DTO class)
function userView(user: User) {
return { id: user.id, name: user.name, email: user.email } // never expose password hash
}
// CONTROLLER — coordinates: parse input, call model, render view
// users.controller.ts
export async function getUser(req: Request, res: Response) {
const user = await usersService.getById(req.params.id)
res.json(userView(user))
}