À propos de ce cours
Chapitre 17 — Les modules et l'organisation du code · 17.4 Organiser un projet JavaScript en modules
• répartir le code selon les responsabilités (affichage, logique, données, utilitaires) ;
• choisir une arborescence de dossiers et des noms de fichiers clairs ;
• respecter le sens des dépendances et éviter les imports circulaires ;
• écrire des modules faciles à tester et à réutiliser.
1. Une responsabilité par module
La règle d'or : un module fait une seule chose, et la fait bien. Si vous avez besoin du mot « et » pour décrire un fichier (« il calcule les moyennes et affiche le tableau et sauvegarde »), il faut le découper.
Questions à se poser pour chaque fonction :
- Touche-t-elle au DOM (la page) ? → couche ui.
- Applique-t-elle une règle métier (calcul de moyenne, prix, validation) ? → couche services.
- Lit-elle ou écrit-elle des données (tableau en mémoire,
localStorage, API) ? → couche data. - Est-elle générique, sans rapport avec ce projet (arrondir, formater une date) ? → utils.
- Est-ce une valeur fixe (clé de stockage, URL d'API, note maximale) ? → config.js.
2. Une arborescence type
mon-projet/
├── index.html
├── css/
│ └── style.css
└── src/
├── main.js ← point d'entrée : initialise et relie les événements
├── config.js ← constantes
├── utils/
│ ├── nombres.js ← fonctions pures, réutilisables ailleurs
│ └── dates.js
├── data/
│ ├── stockage.js ← localStorage
│ └── produits.js ← l'état : liste des produits et ses modifications
├── services/
│ └── panier.js ← règles métier : total, remises, TVA
└── ui/
├── affichage.js ← création du HTML
└── formulaires.js ← lecture des champs
Conventions courantes : noms de fichiers en minuscules avec tirets (export-csv.js) ; un nom qui dit ce que contient le fichier ;
pas de fichier divers.js ou fonctions.js fourre-tout.
3. Le sens des dépendances

Figure 1 — Les couches et le sens autorisé des imports.
Les couches du haut utilisent celles du bas, jamais l'inverse. Conséquences :
- on peut tester
utils/etservices/dans Node.js, sans navigateur, puisqu'ils ne touchent pas au DOM ; - on peut changer complètement l'interface sans toucher aux calculs ;
- on peut remplacer
localStoragepar une API serveur en ne modifiant quedata/.
4. Le piège des imports circulaires
Deux modules qui s'importent mutuellement forment un cycle. Selon l'ordre de chargement, l'un d'eux peut utiliser une valeur de l'autre avant qu'elle n'existe :
📄 panier.js
import { TVA } from "./prix.js";
export const FRAIS_PORT = 7;
export const totalTTC = (ht) => ht * (1 + TVA);
📄 prix.js
import { FRAIS_PORT } from "./panier.js";
export const TVA = 0.19;
// Utilisé IMMÉDIATEMENT, au chargement : panier.js n'a pas encore fini de s'exécuter
export const LIVRAISON = FRAIS_PORT * (1 + TVA);
📄 main.js
import { totalTTC } from "./panier.js";
console.log(totalTTC(100));
Dans le terminal : node main.js (extrait)
file:///…/prix.js:4
export const LIVRAISON = FRAIS_PORT * (1 + TVA);
^
ReferenceError: Cannot access 'FRAIS_PORT' before initialization
main.js charge panier.js, qui charge d'abord prix.js. prix.js s'exécute alors avant la fin de panier.js, et FRAIS_PORT n'existe pas encore. Solution : déplacer les constantes partagées dans un troisième module (config.js)
importé par les deux, pour qu'il n'y ait plus de cycle.
5. Préférer les fonctions pures
// ✗ Dépend d'une variable globale et du DOM : difficile à tester et à réutiliser
function afficherMoyenne() {
const m = eleves.reduce((s, e) => s + e.note, 0) / eleves.length;
document.querySelector("#moy").textContent = m;
}
// ✓ Pure : reçoit ses données, renvoie un résultat. Testable dans Node.js.
export function moyenne(notes) {
return notes.reduce((s, n) => s + n, 0) / notes.length;
}
// …et l'affichage reste dans ui/ :
// zone.textContent = moyenne(eleves.map((e) => e.note));
• Un module = une responsabilité ; un nom de fichier qui la décrit.
• Couches : main → ui / services → data → utils + config.
• Les imports vont du haut vers le bas, jamais l'inverse.
• Un cycle d'imports peut provoquer « Cannot access … before initialization » : extraire la partie commune.
• Fonctions pures dans utils et services : testables sans navigateur.
🛠 Activité 4 — Concevoir l'architecture d'une boutique
Mission : Sans écrire de code, concevez l'organisation en modules d'une petite boutique en ligne : catalogue, filtre par catégorie, panier, code promo, sauvegarde du panier, affichage.
Questions pour avancer (répondez dans votre compte rendu, puis appliquez dans votre projet) :
- Listez toutes les fonctions dont l'application aura besoin (au moins 12). Pour chacune, répondez aux cinq questions de la partie 1 et attribuez-lui une couche.
- Proposez l'arborescence complète des fichiers. Chaque fichier doit pouvoir être décrit en une phrase, sans le mot « et ».
- Dessinez le graphe des imports (une flèche = « importe »). Y a-t-il une flèche qui remonte ? Un cycle ?
- Le calcul du code promo a besoin du nombre d'articles du panier, et le panier a besoin du code promo pour afficher le total. Comment éviter le cycle entre
promo.jsetpanier.js? - Votre chef annonce que le catalogue viendra bientôt d'une API au lieu d'un tableau en dur. Quels fichiers devrez-vous modifier ? Si la réponse est « beaucoup », que faut-il revoir dans votre découpage ?
- Choisissez une fonction de
services/et écrivez trois exemples d'entrée et de sortie attendue. Pourquoi est-ce facile pour cette couche et difficile pourui/?
Je vérifie / je rends :
- ☐ Le tableau fonction → couche.
- ☐ L'arborescence et le graphe des imports (dessin ou outil en ligne).
← 17.3 Les modules dans le navigateur et dans Node.js17.5 Utiliser des paquets npm et un outil de build (Vite) →