Guide complet de l'architecture hexagonale (Ports & Adapters) : isoler la logique métier de l'infrastructure, améliorer la testabilité et construire des systèmes flexibles qui évoluent avec les contraintes techniques.
Table des matières
- 1. Qu'est-ce que l'architecture hexagonale ?
- 2. Pourquoi utiliser les ports et adaptateurs ?
- 3. Concepts fondamentaux
- 4. Ports et adaptateurs en pratique
- 5. Exemple de mise en œuvre
- 6. Architecture hexagonale vs architecture en couches
- 7. Bonnes pratiques
- 8. Quand utiliser l'architecture hexagonale
- 9. Conclusion
1. Qu'est-ce que l'architecture hexagonale ?
L'architecture hexagonale, aussi appelée Ports and Adapters (ports et adaptateurs), a été introduite par Alistair Cockburn. L'objectif est de placer le cœur applicatif (domaine et cas d'usage) au centre du système et de traiter tout le reste — bases de données, interfaces utilisateur, brokers de messages, API tierces — comme des composants branchés via des interfaces bien définies.
Le nom évoque l'idée que l'application peut être abordée sous plusieurs angles (comme les côtés d'un hexagone). Chaque côté représente une façon différente d'interagir avec le monde extérieur : API HTTP, CLI, jobs batch, messagerie, persistance, etc.
Contrairement aux architectures en couches classiques où les dépendances pointent souvent vers l'infrastructure, l'architecture hexagonale applique l'inversion de dépendances : le domaine définit ce dont il a besoin (ports), et l'infrastructure fournit les implémentations (adaptateurs).
2. Pourquoi utiliser les ports et adaptateurs ?
- Testabilité : remplacer bases de données ou API réelles par des adaptateurs en mémoire ou des mocks lors des tests.
- Indépendance technologique : passer de PostgreSQL à MongoDB, ou de REST à gRPC, sans réécrire les règles métier.
- Frontières claires : les ports documentent le contrat entre la logique métier et l'extérieur.
- Évolutivité : ajouter de nouveaux canaux (API mobile, GraphQL, événements) via de nouveaux adaptateurs.
- Alignement avec le DDD : s'accorde naturellement avec le domain-driven design et la clean architecture.
3. Concepts fondamentaux
3.1. Cœur applicatif
Le cœur contient les entités, objets valeur, services de domaine et services applicatifs (cas d'usage). Il ne doit pas dépendre des frameworks, annotations ORM ou détails HTTP.
3.2. Ports
Un port est une interface qui définit comment l'application communique avec l'extérieur.
- Ports entrants (driving) : API exposées à l'extérieur (ex.
PlaceOrderUseCase). - Ports sortants (driven) : abstractions dont l'application a besoin (ex.
OrderRepository,PaymentGateway).
3.3. Adaptateurs
Un adaptateur implémente un port pour une technologie donnée. Les adaptateurs entrants appellent les ports entrants ; les adaptateurs sortants implémentent les ports sortants.
4. Ports et adaptateurs en pratique
4.1. Exemple d'adaptateur entrant
Un contrôleur REST Spring traduit les requêtes HTTP en appels de cas d'usage :
@RestController
@RequestMapping("/orders")
public class OrderController {
private final PlaceOrderUseCase placeOrder;
public OrderController(PlaceOrderUseCase placeOrder) {
this.placeOrder = placeOrder;
}
@PostMapping
public ResponseEntity<OrderResponse> create(@RequestBody CreateOrderRequest request) {
OrderId id = placeOrder.execute(request.toCommand());
return ResponseEntity.status(HttpStatus.CREATED).body(OrderResponse.from(id));
}
}
4.2. Port sortant et adaptateur
// Port sortant (module application)
public interface OrderRepository {
void save(Order order);
Optional<Order> findById(OrderId id);
}
// Adaptateur sortant (module infrastructure)
@Repository
public class JpaOrderRepository implements OrderRepository {
private final OrderJpaRepository jpa;
@Override
public void save(Order order) {
jpa.save(OrderEntity.from(order));
}
@Override
public Optional<Order> findById(OrderId id) {
return jpa.findById(id.value()).map(OrderEntity::toDomain);
}
}
5. Exemple de mise en œuvre
Organisation modulaire typique en Java :
- domain — entités, objets valeur, événements de domaine
- application — cas d'usage, interfaces de ports entrants/sortants
- infrastructure — adaptateurs JPA, REST, messagerie
- bootstrap — configuration Spring, câblage des dépendances
Règle de dépendance : infrastructure → application → domain. Le module domaine n'a aucune dépendance vers les frameworks.
6. Architecture hexagonale vs architecture en couches
Dans un modèle 3 tiers classique (présentation → métier → données), la couche métier dépend souvent des frameworks de persistance. L'architecture hexagonale rend les dépendances explicites via les ports et isole le cœur.
- En couches : organisé par rôle technique (UI, services, DAO).
- Hexagonale : organisé par domaine et direction d'interaction (entrant vs sortant).
Les deux approches peuvent coexister : structurer les packages par couche dans chaque module adaptateur tout en préservant les frontières de ports au niveau applicatif.
7. Bonnes pratiques
- Adaptateurs fins : mapping, validation et traduction de protocole uniquement — pas de règles métier.
- Définir les ports dans la couche application : pas dans l'infrastructure.
- DTO à la frontière : ne pas faire fuiter les entités JPA ou modèles HTTP dans le domaine.
- Tester le cœur sans Spring : tests unitaires purs avec adaptateurs factices.
- Éviter un cœur anémique : placer la logique métier dans le domaine, pas seulement dans les services.
8. Quand utiliser l'architecture hexagonale
Adapté lorsque :
- le domaine est non trivial et durable ;
- vous avez besoin d'une forte couverture de tests sans infrastructure lourde ;
- plusieurs interfaces (API, événements, CLI) partagent les mêmes cas d'usage ;
- l'infrastructure est susceptible de changer (migration cloud, changement de base).
Peut être excessif lorsque :
- l'application est un prototype CRUD simple ;
- l'équipe est petite et le domaine stable et peu complexe ;
- la vitesse de livraison prime sur la modularité à long terme.
9. Conclusion
L'architecture hexagonale aide les équipes à construire des systèmes où la logique métier reste indépendante des frameworks et de l'infrastructure. En définissant des ports et adaptateurs explicites, vous gagnez en flexibilité, en structure et en qualité de tests — surtout dans les domaines complexes qui doivent évoluer sur plusieurs années.
Combinée au DDD et à des règles de dépendance claires, cette approche permet de maîtriser la complexité sans sacrifier la vélocité une fois le patron maîtrisé par l'équipe.
Résumé : Architecture hexagonale (Ports & Adapters)
Problème / besoin : Les applications deviennent souvent fortement couplées aux frameworks, bases de données et interfaces utilisateur, ce qui complique les tests, l'évolution et le remplacement des composants techniques.
Solution proposée : L'architecture hexagonale place le domaine métier au centre et connecte l'extérieur via des ports et adaptateurs explicites, isolant la logique applicative des détails techniques.
Principe d'implémentation : Elle est généralement mise en œuvre avec un cœur domaine entouré d'adaptateurs entrants (contrôleurs REST, listeners) et sortants (repositories, clients API), souvent avec l'inversion de dépendances et des frameworks comme Spring Boot.
0 Comments