Aide en ligne
Tous les articles

Architecture logicielle : découper une application en couches

Quand les développeurs d'un projet partagent la même vision de sa structure, ils retrouvent plus vite où modifier le code, se marchent moins sur les pieds et se comprennent mieux. Le découpage en couches est la vision la plus répandue : vous l'avez probablement déjà vue en cours, et elle sera familière à la plupart des développeurs expérimentés. Ce n'est pas la seule manière de structurer un projet, mais c'est une base solide, et les approches plus récentes (voir plus bas) en sont des variantes.

Les cinq couches

Une application peut se décrire comme une pile de cinq couches. Chacune a une responsabilité précise et s'appuie sur celle qui est en dessous.

Les cinq couches d'une application De haut en bas : frontière, contrôleur, métier, persistance, base de données. Chaque couche utilise celle qui se trouve juste en dessous. Frontière présentation, IHM : affichage et saisies Contrôleur cas d'utilisation : enchaîne les étapes Métier domaine : entités et règles de gestion Persistance accès aux données : requêtes, fichiers Base de données
Chaque flèche signifie « utilise » : une couche appelle celle du dessous, jamais l'inverse.
CoucheSon rôleElle ne doit pas contenir
Frontière (présentation, IHM) Afficher les données et recueillir les saisies : pages HTML, écrans, réponses JSON d'une API. de règles de gestion, de requêtes SQL
Contrôleur Dérouler un cas d'utilisation (« réserver un livre ») : il appelle les objets métier, fait appel à la persistance et fournit à la frontière ce qu'elle doit afficher. de règles de gestion détaillées, de SQL, de HTML
Métier (domaine) Les objets propres au client (abonné, livre, commande) et leurs règles : « un abonné ne peut pas réserver plus de 3 livres ». d'affichage, de SQL, de dépendance à une page web
Persistance Savoir comment accéder aux données : connexion à la base, requêtes, lecture d'un fichier XML, appel d'un service externe. de règles de gestion
Base de données Le système de gestion de base de données lui-même : tables, contraintes, index, et parfois des morceaux du projet (déclencheurs, procédures stockées). de logique cachée qu'aucun document ne décrit

Le mot « contrôleur » est ici pris au sens de la logique des cas d'utilisation. Dans un framework web de type MVC, cette logique est souvent répartie entre un contrôleur HTTP très fin (il lit la requête et choisit la réponse) et des classes de service.

La règle des dépendances

C'est le point le plus important de ce découpage : chaque couche ne connaît que celle qui est juste en dessous.

Cette discipline a des conséquences très concrètes : on peut changer l'apparence d'un écran sans toucher aux règles de gestion, ajouter une API à côté du site en réutilisant le même métier, ou changer de base de données en ne modifiant qu'une couche.

Un exemple : réserver un livre

Dans une bibliothèque municipale, un abonné réserve un livre sur le site. Voici le chemin de la demande :

  1. La frontière reçoit le clic et l'identifiant du livre, et les transmet au contrôleur.
  2. Le contrôleur demande à la persistance de charger le livre.
  3. Il demande à l'objet métier Abonne de réserver ce livre. Les règles de gestion s'appliquent : le livre est-il disponible ? l'abonné a-t-il atteint sa limite ?
  4. Si la réservation est acceptée, le contrôleur demande à la persistance de l'enregistrer.
  5. La persistance exécute la requête SQL sur la base de données.
  6. Le contrôleur renvoie le résultat à la frontière, qui l'affiche.

Le même parcours en PHP, volontairement simplifié (sans constructeurs ni gestion des erreurs techniques) :

<?php
// Frontière : reservation.php (affichage et saisie uniquement)
$resultat = $controleur->reserver((int) $_POST['livre'], $abonneConnecte);
echo htmlspecialchars($resultat);

// Contrôleur : déroule le cas d'utilisation
class ReservationControleur {
    public function reserver($idLivre, Abonne $abonne) {
        $livre = $this->livres->trouver($idLivre);              // persistance
        if ($livre === null) {
            return 'Livre introuvable.';
        }
        try {
            $abonne->reserver($livre);                          // règles métier
        } catch (RegleMetierException $e) {
            return $e->getMessage();
        }
        $this->reservations->enregistrer($abonne, $livre);      // persistance
        return 'Réservation enregistrée.';
    }
}

// Métier : les règles de gestion, sans SQL ni HTML
class Abonne {
    public function reserver(Livre $livre) {
        if ($this->nbReservations >= 3) {
            throw new RegleMetierException('Trois réservations au maximum.');
        }
        if (!$livre->estDisponible()) {
            throw new RegleMetierException('Ce livre est déjà réservé.');
        }
        $this->nbReservations++;
        $livre->marquerReserve();
    }
}

// Persistance : le seul endroit qui connaît le SQL
class LivreRepository {
    public function trouver($id) {
        $st = $this->pdo->prepare('SELECT * FROM livres WHERE id = ?');
        $st->execute(array($id));
        $ligne = $st->fetch(PDO::FETCH_ASSOC);
        return $ligne ? Livre::depuisLigne($ligne) : null;
    }
}

Une variante courante donne aux classes métier la responsabilité de se charger et de se sauvegarder elles-mêmes, comme décrit dans l'article Architecture métier. Le principe reste le même : l'accès aux données doit rester dans un endroit identifiable, et non éparpillé dans les pages.

Les erreurs fréquentes

Est-ce adapté à tous les projets ?

Pas toujours. Un script de vingt lignes n'a pas besoin de cinq couches, et un découpage trop poussé alourdit un petit projet. Il se justifie dès qu'un projet doit durer, être testé ou être partagé entre plusieurs développeurs. Gardez au minimum la séparation entre l'affichage, les règles de gestion et l'accès aux données : c'est elle qui apporte l'essentiel du bénéfice.

Le découpage facilite aussi les tests. Le métier se teste sans base de données ni navigateur, avec de simples objets ; la persistance se teste avec une base de test ; le contrôleur, avec un faux accès aux données.

Les couches se reflètent naturellement dans l'arborescence du projet, un dossier par couche ou par module : voir l'article Structure de fichiers d'un projet web.

Le lien avec MVC et les autres architectures

ApprocheCorrespondance avec les couches
MVC La vue correspond à la frontière, le modèle au métier et à la persistance, et le contrôleur MVC joue un rôle plus étroit que celui décrit ici : il gère la requête web.
Architecture hexagonale, architecture « propre » Même idée de séparation, avec une dépendance inversée : le métier ne dépend de rien, et c'est la persistance qui dépend du métier, par l'intermédiaire d'une interface. On peut ainsi remplacer la base de données ou l'interface sans toucher aux règles.

Que vous suiviez l'une ou l'autre, l'idée directrice est la même : séparer ce qui change pour des raisons différentes (l'affichage, les règles du client, le stockage), et faire en sorte que les dépendances aillent dans un seul sens.

Pour aller plus loin