Aller au contenu principal

Architecture multi-tiers

Organiser une application en couches séparées pour la rendre maintenable et évolutive

Notions théoriques

L'architecture multi-tiers (ou multi-couches) consiste à diviser une application en plusieurs couches ayant chacune une responsabilité précise. Chaque couche ne communique qu'avec les couches adjacentes.

Principes fondamentaux

  • Séparation des responsabilités : chaque couche a un rôle unique
  • Couplage faible : les couches sont indépendantes les unes des autres
  • Réutilisabilité : les couches métier peuvent être réutilisées par différentes interfaces (web, API, CLI)
  • Testabilité : chaque couche peut être testée isolément

Les couches principales

CoucheRôleExemples dans Symfony
PrésentationAfficher les données, gérer les interactions utilisateurControllers, Templates Twig
Métier (Business)Implémenter les règles métier, orchestrer les traitementsServices
Accès aux donnéesPersister et récupérer les donnéesRepositories, Entities

Flux d'une requête

Navigateur → Controller → Service → Repository → Base de données
← View ← Service ← Repository ←

Exemple de mise en application

Dans notre jeu, la création d'un personnage suit ce flux :

  1. Couche Présentation — le controller reçoit la requête et délègue :
#[Route('/game/create-character/{playerId}')]
public function createCharacter(int $playerId, CharacterService $characterService): Response
{
$character = $characterService->createForPlayer($playerId);
return $this->render('character/created.html.twig', ['character' => $character]);
}
  1. Couche Métier — le service applique les règles métier :
class CharacterService
{
public function createForPlayer(int $playerId): Character
{
$player = $this->playerRepository->find($playerId);
if ($player === null) {
throw new \InvalidArgumentException('Joueur introuvable');
}
$character = new Character();
$character->setName('Nouveau Personnage');
$character->setPlayer($player);
$this->characterRepository->save($character, true);
return $character;
}
}
  1. Couche Accès aux données — le repository interagit avec la base de données :
class CharacterRepository extends ServiceEntityRepository
{
public function save(Character $character, bool $flush = false): void
{
$this->getEntityManager()->persist($character);
if ($flush) {
$this->getEntityManager()->flush();
}
}
}

Test de mémorisation/compréhension


Quel est le principe fondamental de l'architecture multi-tiers ?


Quelle couche contient les règles métier dans Symfony ?


Dans l'architecture multi-tiers, le Controller doit-il contenir des requêtes SQL ?


Quel avantage offre la séparation en couches pour les tests ?



TP pour réfléchir et résoudre des problèmes

Restructurez votre application en respectant l'architecture multi-tiers

Étape 1 : Dans CharacterService, complétez la méthode createForPlayer en associant le nouveau personnage au joueur.

Étape 2 : Dans le GameController, injectez et utilisez CharacterService plutôt que d'accéder directement au repository.


Bonne pratique - Le Controller ne doit pas connaitre la base de données

Un Controller dont la méthode fait plus de 10 lignes signale souvent que de la logique métier s'est glissée dans la mauvaise couche. Déplacez cette logique dans un Service. Le Controller se limite à : recevoir la requête, appeler le Service, retourner la réponse.

Une solution