La couche modèle
Rôle du modèle dans une architecture MVC, validation des données, distinction entre services et contrôleurs, et pourquoi ne pas écrire de SQL dans un contrôleur.
Le modèle est le cœur de l'application : c'est lui qui sait ce qu'est une donnée, comment elle se valide et comment on y accède. Bien isolé, il rend le reste du code plus simple et plus sûr.
🎯 Le rôle du modèle
Un modèle porte trois responsabilités :
- décrire la structure d'une donnée (ses champs, ses types) ;
- valider les entrées avant de toucher à la base ;
- accéder aux données (lecture, écriture, recherche).
// models/article.js
export function validateArticle(data) {
const errors = []
if (typeof data.titre !== "string" || data.titre.length === 0)
errors.push("Le titre est requis.")
if (typeof data.prix !== "number" || data.prix < 0)
errors.push("Le prix doit être un nombre positif.")
return errors
}✅ Valider au plus tôt
Validez à la frontière (quand les données entrent), pas au milieu d'une requête SQL. Cela évite les données corrompues et les erreurs difficiles à tracer.
Ne faites confiance à aucune donnée venant du client. Toujours valider req.body avant de l'insérer ou de l'utiliser.
🧩 Services vs contrôleurs
Ces deux couches sont souvent confondues. Voici la distinction :
| Couche | Parle HTTP ? | Contient l'accès BDD ? | Réutilisable hors web ? |
|---|---|---|---|
| Contrôleur | Oui | Non (délègue) | Non |
| Service | Non | Non (appelle le modèle) | Oui |
| Modèle | Non | Oui | Oui |
// services/articleService.js
import { Article } from "../models/article.js"
export async function publierArticle(data) {
const errors = Article.validate(data)
if (errors.length) throw new Error(errors.join(" "))
return Article.create({ ...data, publieLe: new Date() })
}Le service contient la règle « publier = valider + horodater ». Le contrôleur se contente d'appeler publierArticle et de renvoyer le résultat.
🚫 Pas de SQL dans le contrôleur
Mélanger SQL et logique HTTP crée trois problèmes :
- le contrôleur devient énorme et illisible ;
- la même requête est dupliquée à plusieurs endroits ;
- on ne peut pas tester la logique sans démarrer un serveur.
// ❌ À éviter
app.post("/articles", async (req, res) => {
const { titre, prix } = req.body
await db.query("INSERT INTO articles (titre, prix) VALUES ($1, $2)", [titre, prix])
res.status(201).json({ ok: true })
})// ✅ Préférer
app.post("/articles", async (req, res) => {
try {
const article = await publierArticle(req.body) // service
res.status(201).json(article)
} catch (e) {
res.status(400).json({ error: e.message })
}
})➡️ Suite
La page Introduction aux ORM montre comment déléguer l'accès aux données à un ORM au lieu d'écrire du SQL.
Quiz du chapitre
MVC & ORM
Terminez le quiz du chapitre pour le marquer comme complété.
Architecture MVC
Séparation des responsabilités en Modèle, Vue et Contrôleur, et application concrète du pattern MVC dans une API Express avec une arborescence routes/controllers/models/services.
Introduction aux ORM
Qu'est-ce qu'un ORM/ODM, le mapping objet-relationnel, ses avantages et limites, la comparaison avec le SQL brut, et le panorama Prisma, Sequelize, TypeORM et Mongoose.