Organize the codebase into layers — presentation, business, data — where each layer only depends on the one directly below it.
Without an agreed structure, a controller might query the database directly, a data-access class might contain business rules, and a service might format HTML — the codebase becomes a web of dependencies where changing one thing risks breaking something in a completely different concern.
Presentation depends on business logic; business logic depends on data access; data access depends on nothing above it. Each layer only calls downward, never upward or sideways past its neighbor — the presentation layer never touches the database directly.
The controller never imports the repository directly — it only ever calls through the service layer sitting between them.
// Data access layer — knows about storage, nothing else.
const orderRepository = {
save: (order) => console.log(`db: saved order ${order.id}`),
};
// Business logic layer — knows about rules, calls data access.
const orderService = {
placeOrder(items) {
if (items.length === 0) throw new Error("order must have items");
const order = { id: "order-1", items };
orderRepository.save(order);
return order;
},
};
// Presentation layer — knows about requests/responses, calls business logic.
function handleCreateOrderRequest(body) {
const order = orderService.placeOrder(body.items);
return { status: 201, body: order }; // never touches orderRepository directly
}
console.log(handleCreateOrderRequest({ items: ["book", "pen"] }));