Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

Organiser un projet JavaScript en modules

Organiser un projet JavaScript en modules. Dans cette leçon du cours JavaScript, vous apprendrez à : répartir le code selon les responsabilités (affichage, logique, données, utilitaires) ; choisir une...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Organiser un projet JavaScript en modules. Dans cette leçon du cours JavaScript, vous apprendrez à : 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.

Chapitre 17 — Les modules et l'organisation du code · 17.4 Organiser un projet JavaScript en modules

🎯 Objectifs de la leçon
• 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

Architecture en couches d'un projet JavaScript
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/ et services/ 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 localStorage par une API serveur en ne modifiant que data/.

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));
ℹ️ À retenir
• 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) :

  1. 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.
  2. Proposez l'arborescence complète des fichiers. Chaque fichier doit pouvoir être décrit en une phrase, sans le mot « et ».
  3. Dessinez le graphe des imports (une flèche = « importe »). Y a-t-il une flèche qui remonte ? Un cycle ?
  4. 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.js et panier.js?
  5. 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 ?
  6. 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 pour ui/?

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) →