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
| Couche | Rôle | Exemples dans Symfony |
|---|---|---|
| Présentation | Afficher les données, gérer les interactions utilisateur | Controllers, Templates Twig |
| Métier (Business) | Implémenter les règles métier, orchestrer les traitements | Services |
| Accès aux données | Persister et récupérer les données | Repositories, 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 :
- 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]);
}
- 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;
}
}
- 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
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.
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
Vous devez être connecté pour voir le contenu.