Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 812 minIntermédiaire

Architecture hexagonale et clean architecture : aperçu

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • expliquer le principe des ports et des adaptateurs ;
  • distinguer adaptateurs entrants et sortants ;
  • comparer architecture hexagonale, clean architecture et architecture en couches ;
  • juger si un projet a besoin de ce découpage.

3 exercices corrigés1 schéma

L'architecture hexagonale, ou architecture ports et adaptateurs, place les règles de gestion au centre de l'application et tout le reste autour : l'interface web, l'application de bureau, la base de données, l'envoi d'e-mails. Le centre définit des ports (des interfaces), et chaque technologie s'y branche par un adaptateur. Toutes les dépendances pointent vers le centre, jamais vers l'extérieur. Ce chapitre est un aperçu : il te donne de quoi comprendre un article ou une offre de stage qui en parle, et reconnaître que ResaSalles en applique déjà l'essentiel.

Le principe

Alistair Cockburn a décrit ce style en 2005 sous le nom Ports and Adapters. Le nom « hexagonale » vient du dessin qu'il en faisait : l'hexagone n'a rien de particulier, il laisse simplement de la place pour dessiner plusieurs adaptateurs sur chaque côté. L'idée tient en trois points :

  1. Le cœur (le domaine et les cas d'utilisation) contient les règles de gestion. Il ne connaît aucune technologie : ni HTTP, ni SQL, ni WinForms.
  2. Les ports sont les interfaces du cœur. Les ports entrants décrivent ce que l'application sait faire (« réserver une salle ») ; les ports sortants décrivent ce dont elle a besoin (« savoir si un créneau est libre », « enregistrer une réservation », « connaître l'heure », « envoyer une confirmation »).
  3. Les adaptateurs relient une technologie à un port. Les adaptateurs entrants (ou primaires) appellent le cœur : un contrôleur Express, un formulaire WinForms, un script en ligne de commande, un test. Les adaptateurs sortants (ou secondaires) sont appelés par le cœur : un Repository MySQL, un Repository en mémoire, un client SMTP.
CœurReservationServiceSalle · Reservationport entrantreserver()ports sortantsReservationRepositorySalleRepositoryHorlogeNotificationaucune technologie iciadaptateurs entrantsContrôleurExpress (HTTP)FormulaireWinFormsScriptligne de commandeTestsautomatisésadaptateurs sortantsRepositoryMySQLRepositoryen mémoireHorlogesystèmeE-mailsSMTPtoutes les dépendances pointent vers le cœur
Ports et adaptateurs : la technologie à l'extérieur, les règles au centre

ResaSalles est déjà presque hexagonale

Reprends le code des chapitres 6 et 7 et range chaque pièce :

Élément de l'architecture hexagonaleDans ResaSalles
Cœur (cas d'utilisation et règles)ReservationService, les entités Salle et Reservation
Port entrantLa méthode reserver(demande) du service
Ports sortantsIReservationRepository, ISalleRepository, TimeProvider (l'horloge)
Adaptateurs entrantsLe contrôleur Express, le formulaire WinForms et son contrôleur, les tests
Adaptateurs sortantsReservationRepository (MySQL), FausseReservations (mémoire), TimeProvider.System
Configuration (qui branche quoi)La racine de composition : app.js, Program.cs

L'injection de dépendances du chapitre 7 est le mécanisme qui rend l'hexagone possible : le service dépend d'interfaces, et la racine de composition choisit les adaptateurs.

Ajouter un adaptateur entrant

Brévent Industrie veut importer chaque nuit les réservations des salles de formation, à partir d'un script lancé par une tâche planifiée. Avec une architecture hexagonale, c'est un nouvel adaptateur entrant : un script qui lit ses paramètres et appelle le même service.

js
// scripts/reserver.js : un deuxième adaptateur entrant, la ligne de commande.
import { parseArgs } from 'node:util';
import { pool } from '../src/db.js';
import { ErreurMetier } from '../src/erreurs.js';
import { creerSalleRepository } from '../src/repositories/salle.repository.js';
import { creerReservationRepository } from '../src/repositories/reservation.repository.js';
import { creerReservationService } from '../src/services/reservation.service.js';

const { values } = parseArgs({
  options: {
    salle: { type: 'string' },
    employe: { type: 'string' },
    debut: { type: 'string' },
    fin: { type: 'string' },
    objet: { type: 'string' },
    participants: { type: 'string', default: '1' },
  },
});

const service = creerReservationService({
  salleRepository: creerSalleRepository(pool),
  reservationRepository: creerReservationRepository(pool),
});

try {
  const r = await service.reserver({
    salleId: Number(values.salle),
    employeId: Number(values.employe),
    debut: new Date(values.debut),
    fin: new Date(values.fin),
    objet: values.objet,
    participants: Number(values.participants),
  });
  console.log(`Réservation n° ${r.id} enregistrée`);
} catch (err) {
  if (!(err instanceof ErreurMetier)) throw err;
  console.error(`Refusée : ${err.message}`);
  process.exitCode = 1;
} finally {
  await pool.end();
}
text
$ node --env-file=.env scripts/reserver.js --salle 1 --employe 7 --debut 2026-12-03T14:00 --fin 2026-12-03T15:00 --objet "Formation sécurité"
Réservation n° 2 enregistrée
$ node --env-file=.env scripts/reserver.js --salle 1 --employe 7 --debut 2026-12-03T10:00 --fin 2026-12-03T11:00 --objet "Formation"
Refusée : La salle est déjà réservée sur ce créneau

parseArgs (module node:util) lit les options --salle 1. Le script traduit le résultat dans le langage de la ligne de commande : un message sur la sortie standard en cas de succès, un message sur la sortie d'erreur et un code de sortie non nul en cas de refus, pour que la tâche planifiée sache que quelque chose a échoué. C'est exactement le travail d'un contrôleur, avec un autre protocole que HTTP. Aucune règle n'a été recopiée.

Ajouter un adaptateur sortant

Nouvelle demande : envoyer un e-mail de confirmation après chaque réservation. Le cœur déclare un port sortant, notification.confirmer(reservation), et ignore tout du SMTP. Un adaptateur SMTP le réalise en production ; dans les tests, un faux qui garde les messages dans une liste ; plus tard, peut-être, un adaptateur qui poste un message dans la messagerie d'équipe. Le service n'a pas à changer quand on change d'adaptateur.

Clean architecture et architecture en oignon

Deux styles très proches portent d'autres noms :

  • L'architecture en oignon (onion architecture, Jeffrey Palermo, 2008) dessine des cercles concentriques : le modèle du domaine au centre, puis les services du domaine, puis les services applicatifs, puis l'infrastructure et l'interface à l'extérieur.
  • La clean architecture (Robert C. Martin, 2012) reprend les mêmes cercles : entités, cas d'utilisation, adaptateurs d'interface, frameworks et pilotes. Sa règle de dépendance dit que le code source ne peut dépendre que de cercles plus intérieurs.

Les trois styles partagent la même idée : isoler les règles de gestion de la technique, et inverser les dépendances pour que la technique dépende du métier. Ils diffèrent surtout par le vocabulaire et le nombre de cercles.

Couches ou hexagone ?

Architecture en couchesArchitecture hexagonale
ImageUne pile : présentation, métier, donnéesUn centre et des adaptateurs autour
Sens des dépendancesVers le bas : le métier dépend de l'accès aux donnéesVers le centre : l'accès aux données dépend du métier (il implémente ses interfaces)
La base de donnéesTout en bas, fondation de l'applicationUn adaptateur parmi d'autres, remplaçable
Plusieurs interfacesPossible, la présentation se multipliePrévu dès le départ : autant d'adaptateurs entrants que nécessaire
CoûtFaible, facile à expliquerPlus d'interfaces et de fichiers, plus de vocabulaire

Dans la version en couches « naïve », le service importe directement le Repository MySQL : il dépend de la couche du dessous. Dès qu'on fait dépendre le service d'une interface (chapitre 7), on a inversé cette dépendance, et l'architecture en couches devient, dans les faits, hexagonale. C'est pour cela que la frontière entre les deux est floue dans les projets réels.

Quand est-ce utile ?

L'architecture hexagonale paie quand les règles de gestion sont riches, que l'application doit vivre longtemps et qu'elle a plusieurs portes d'entrée ou de sortie (une API, une application de bureau, des imports, plusieurs bases). Pour un formulaire qui enregistre des contacts dans une table, c'est un excès : trois couches, ou même un MVC simple, suffisent.

Pour un projet de BTS, retiens la démarche plutôt que le nom : des règles isolées dans un service, des interfaces pour ce qui doit pouvoir être remplacé (la base, l'horloge, les e-mails), une racine de composition qui branche le tout. Si la commission te demande si ton application est hexagonale, tu peux répondre honnêtement : « elle en applique le principe, les règles de gestion ne dépendent d'aucune technologie, et je l'ai prouvé avec des tests qui tournent sans base ».

Exercices

Exercice 1 sur 3 Entrant ou sortant ? Réussi À revoir

Classe chaque élément : port entrant, port sortant, adaptateur entrant ou adaptateur sortant.

  1. Un contrôleur ASP.NET Core qui reçoit POST /api/reservations.
  2. L'interface INotification avec la méthode Confirmer(Reservation r).
  3. La classe SmtpNotification, qui implémente INotification avec un serveur de messagerie.
  4. La méthode ReservationService.Annuler(int id, int employeId).
  5. Un test unitaire qui appelle le service.
  6. Une classe ReservationRepositoryPostgres.
Voir la réponseMasquer la réponse de l'exercice 1
  1. Adaptateur entrant : il traduit HTTP en appel au cœur.
  2. Port sortant : un besoin du cœur, exprimé sans technologie.
  3. Adaptateur sortant : il réalise le port avec une technologie précise.
  4. Port entrant : un cas d'utilisation que le cœur offre.
  5. Adaptateur entrant : un test pilote le cœur comme le ferait une interface.
  6. Adaptateur sortant : une autre réalisation du port d'accès aux réservations.

Tu avais trouvé ?

Exercice 2 sur 3 Trouver la violation Réussi À revoir

Dans un projet qui se dit hexagonal, ReservationService.cs commence par ces lignes. Lesquelles violent la règle de dépendance, et comment corriger ?

csharp
using System.Net.Mail;
using ResaSalles.Models;
using MySqlConnector;
using Microsoft.AspNetCore.Mvc;
Voir la réponseMasquer la réponse de l'exercice 2
  • using System.Net.Mail; : le cœur envoie lui-même des e-mails. Il faut un port INotification et un adaptateur SMTP.
  • using MySqlConnector; : le cœur parle à MySQL. Il faut passer par IReservationRepository.
  • using Microsoft.AspNetCore.Mvc; : le cœur dépend du framework web (il renvoie sans doute des IActionResult). Il doit renvoyer des objets métier ou lever des exceptions métier ; le contrôleur traduit en HTTP.

using ResaSalles.Models; est correct : les entités font partie du cœur.

Tu avais trouvé ?

Exercice 3 sur 3 Un nouvel adaptateur Réussi À revoir

La direction veut que l'accueil puisse consulter le planning des salles sur un écran d'affichage qui interroge l'application toutes les minutes, et réserver depuis une borne tactile. Quels adaptateurs faut-il ajouter, et que faut-il modifier dans le cœur ?

Voir la réponseMasquer la réponse de l'exercice 3

Il faut ajouter des adaptateurs entrants : une route de lecture du planning dans l'API (ou une page dédiée) pour l'écran d'affichage, et l'interface de la borne, qui appelle la même API ou le même service. Le cœur ne change pas si le cas d'utilisation « consulter le planning » existe déjà ; sinon, on ajoute ce cas d'utilisation (une méthode de lecture du service ou du Repository) une fois, et tous les adaptateurs en profitent. La règle « pas de réservation dans le passé » s'applique à la borne sans une ligne de plus.

Tu avais trouvé ?

Chapitre terminé !

Fais les 3 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Architecture client-serveur, 2 tiers, 3 tiers et n-tiersRevoir le sommaire du cours