Guide Architecture

Architecture Hexagonale
Ports, Adapters et inversion de dépendances.

L'architecture hexagonale (Ports & Adapters) isole votre domaine métier de l'infrastructure technique. Ce guide couvre les principes fondamentaux, la distinction Driving/Driven, l'inversion de dépendances et la comparaison avec l'architecture en couches, avec des exemples Java concrets.

Contexte

Pourquoi l'architecture hexagonale ?

Dans une application classique, le code métier finit par dépendre directement de la base de données, du framework web, du système de messaging. Changer de technologie de persistance ou de mode d'exposition revient à réécrire une partie du domaine.
L'architecture hexagonale, proposée par Alistair Cockburn en 2005 sous le nom de "Ports and Adapters", résout ce problème en inversant les dépendances : le domaine définit ses besoins via des interfaces (ports), et l'infrastructure fournit des implémentations (adapters). Le domaine ne sait pas qui l'appelle ni où il persiste.
En 2026, avec l'essor du cloud, des architectures modulaires et du déploiement continu, ce pattern est devenu un standard pour les applications Java d'entreprise. Il permet de tester le domaine sans infrastructure, de remplacer une technologie sans toucher au métier, et de raisonner clairement sur les responsabilités de chaque couche.

Hexagonal n'est pas microservices

L'architecture hexagonale est un pattern d'organisation du code au sein d'une application. Elle ne préjuge pas du style de déploiement : monolithe, modulaire, microservices. Un monolithe bien structuré en hexagonal est souvent préférable à des microservices mal découpés.

Modèle

Le domaine au centre

L'architecture hexagonale se structure autour d'un principe simple : le domaine métier est au centre, entouré de ports qui définissent ses contrats, et d'adapters qui les implémentent. Toutes les dépendances pointent vers le domaine.

Le domaine

Le domaine contient la logique métier pure : agrégats, entités, value objects, domain events, domain services. Il n'a aucune dépendance vers l'extérieur : pas de framework, pas de base de données, pas de HTTP. C'est le modèle DDD au centre de l'architecture.

Les ports

Les ports sont les interfaces que le domaine expose (ports entrants) ou dont il dépend (ports sortants). Un port entrant définit un cas d'usage : PlaceOrderUseCase. Un port sortant définit un besoin : OrderRepository. Le domaine ne connaît que ces interfaces.

Les adapters

Les adapters sont les implémentations concrètes des ports. Un adapter REST appelle un port entrant. Un adapter JPA implémente un port sortant. Ils traduisent entre le monde technique et le domaine. On peut les remplacer sans toucher au domaine.

Le domaine ne sait pas QUI l'appelle (un contrôleur REST ? un test ? une CLI ?) ni OÙ il persiste (PostgreSQL ? MongoDB ? un fichier ?). Cette ignorance est précisément ce qui le rend testable, portable et maintenable.

Diagramme C4 Component : Driven Side et Infrastructure Layer d'une application bancaire
Diagramme C4 Component montrant les driven ports et l'infrastructure layer avec les liens d'implémentation entre ports et adapters
Directions

Driving vs Driven : deux directions, un même principe

L'architecture hexagonale distingue deux types de ports et d'adapters selon la direction du flux. Les ports entrants (Driving) exposent les cas d'usage. Les ports sortants (Driven) déclarent les dépendances du domaine. Cette symétrie clarifie les responsabilités.
DirectionCôtéPortsAdaptersExemples
Driving (entrant)GaucheCas d'usageContrôleursPlaceOrderUseCase → REST Controller
Driven (sortant)DroiteDépendancesImplémentationsOrderRepository → JPA Adapter

Driving Port : le cas d'usage

Un Driving Port définit un cas d'usage exposé par le domaine. C'est l'interface que les adapters entrants (REST, CLI, event listener) appellent pour déclencher une action métier.

PlaceOrderUseCase.java
/** Port entrant : cas d'usage "passer une commande". */
public interface PlaceOrderUseCase {
OrderConfirmation placeOrder(PlaceOrderCommand command);
}
/** Commande portant les données nécessaires. */
public record PlaceOrderCommand(
CustomerId customerId,
List<OrderLineRequest> lines
) {}

Driven Port : la dépendance du domaine

Un Driven Port déclare une dépendance du domaine vers l'extérieur. Le domaine définit l'interface, l'infrastructure fournit l'implémentation.

OrderRepository.java & PaymentGateway.java
/** Port sortant : persistance des commandes. */
public interface OrderRepository {
void save(Order order);
Optional<Order> findById(OrderId id);
List<Order> findByCustomer(CustomerId customerId);
}
/** Port sortant : notification de paiement. */
public interface PaymentGateway {
PaymentResult authorize(OrderId orderId, Money amount);
}
Flux de dépendances : Driving Adapters → Ports → Application Services → Domain ← Driven Ports ← Driven Adapters
Living documentation HexaGlue : diagramme de dépendances montrant le flux depuis les Driving Adapters vers le Domain et les Driven Adapters vers les Driven Ports
Principe clé

Le domaine définit, l'infrastructure implémente

L'inversion de dépendances est le mécanisme central de l'architecture hexagonale. Dans une architecture en couches classique, la couche métier dépend de la couche d'accès aux données. En hexagonal, c'est l'inverse : le domaine définit les interfaces (ports), et l'infrastructure les implémente (adapters).
PlaceOrderService.java
/** Service applicatif : orchestre le cas d'usage. */
public class PlaceOrderService implements PlaceOrderUseCase {
// Le domaine DÉFINIT les interfaces (ports)
private final OrderRepository orderRepository;
private final PaymentGateway paymentGateway;
private final PricingService pricingService;
// L'infrastructure IMPLÉMENTE via injection de dépendances
public PlaceOrderService(
OrderRepository orderRepository,
PaymentGateway paymentGateway,
PricingService pricingService) {
this.orderRepository = orderRepository;
this.paymentGateway = paymentGateway;
this.pricingService = pricingService;
}
@Override
public OrderConfirmation placeOrder(PlaceOrderCommand command) {
var order = Order.create(command.customerId());
command.lines().forEach(line ->
order.addLine(line.product(), line.quantity())
);
Money total = pricingService.calculate(order);
PaymentResult payment = paymentGateway.authorize(order.id(), total);
if (payment.isAuthorized()) {
OrderPlaced event = order.confirm();
orderRepository.save(order);
return OrderConfirmation.success(order.id(), event);
}
return OrderConfirmation.paymentDeclined(order.id());
}
}
JpaOrderRepository.java
/** Driven Adapter : implémentation JPA du port OrderRepository. */
@Repository
public class JpaOrderRepository implements OrderRepository {
private final SpringDataOrderRepository springRepo;
private final OrderMapper mapper;
@Override
public void save(Order order) {
var entity = mapper.toJpaEntity(order);
springRepo.save(entity);
}
@Override
public Optional<Order> findById(OrderId id) {
return springRepo.findById(id.value())
.map(mapper::toDomainObject);
}
}

Tester sans infrastructure

Grâce à l'inversion de dépendances, le PlaceOrderService peut être testé avec des mocks des ports sortants : un OrderRepository en mémoire et un PaymentGateway stub. Aucune base de données, aucun service externe, aucune configuration Spring : le test vérifie la logique métier pure en quelques millisecondes.

Comparaison

Hexagonal vs Couches : quand choisir ?

L'architecture en couches (Présentation → Service → Persistance) est le pattern le plus répandu. L'architecture hexagonale propose une alternative centrée sur le domaine. Chacune a ses forces et ses cas d'usage.
CritèreArchitecture en couchesArchitecture hexagonale
CouplageFort : le métier dépend de la persistanceFaible : le domaine ne dépend de rien
TestabilitéTests souvent liés à l'infra (DB, Spring Context)Domaine testable sans infrastructure
Remplacement d'infraDifficile : changements en cascadeSimple : changer un adapter suffit
Complexité initialeFaible : structure familièreModérée : plus d'interfaces et de packages
Cas d'usage idéalCRUD simple, prototypage rapideLogique métier riche, évolution long terme

L'architecture hexagonale n'est pas toujours nécessaire. Pour une application CRUD avec peu de logique métier, l'architecture en couches est plus simple et suffit. L'hexagonal prend tout son sens lorsque le domaine est complexe, que les règles métier évoluent et que vous avez besoin de tester le métier indépendamment de l'infrastructure.

Tests

Tester sans infrastructure

L'un des avantages majeurs de l'architecture hexagonale est la stratégie de test qu'elle rend possible. Chaque couche se teste indépendamment, avec le bon niveau d'isolation.

Tests du domaine

Le domaine se teste en logique pure. Les ports sortants sont mockés ou stubés. Pas de base de données, pas de framework : les tests vérifient les invariants métier, les transitions d'état et les événements émis. Ils s'exécutent en millisecondes.

Tests d'intégration

Chaque adapter se teste séparément. Un test d'intégration pour JpaOrderRepository utilise une base de données réelle (H2 ou Testcontainers). Un test pour le contrôleur REST utilise MockMvc. Un adapter à la fois, avec son infrastructure réelle.

Tests end-to-end

Les tests end-to-end traversent le pipeline complet : un appel via le driving adapter (REST), à travers le domaine, jusqu'aux driven adapters (JPA, messaging). Ils sont peu nombreux, ciblent les scénarios critiques et valident l'assemblage des composants.

HexaGlue

De la théorie à l'automatisation

HexaGlue opère à l'intersection du DDD et de l'architecture hexagonale. Il analyse votre domaine, détecte les ports, génère les adapters d'infrastructure et audite la conformité architecturale, le tout à chaque build Maven.
Ce que fait HexaGlueFonctionnalitéEn savoir plus
Détecte les Driving et Driven PortsClassification automatiqueClassification
Génère les Driven Adapters JPAGénération de codeGénération JPA
Vérifie les dépendances entre couchesAudit d'architectureAudit
Documente la topologieLiving DocumentationLiving Doc

Détection, génération, audit

HexaGlue détecte automatiquement les ports (Driving et Driven), génère les adapters JPA à partir du modèle de domaine, et audite la conformité de l'architecture hexagonale à chaque build. Les violations sont signalées dans le rapport d'audit avec leur sévérité et des pistes de remédiation.

Living documentation : Driving Port (ManagingAccounts) et Driven Ports (AccountRepository, AccountNotifier)
Living documentation HexaGlue : ports entrants (ManagingAccounts avec méthodes deposit, withdraw) et ports sortants (AccountRepository, AccountNotifier) avec signatures

L'architecture hexagonale protège le modèle DDD des détails d'infrastructure. HexaGlue automatise la partie technique : vous écrivez le domaine, il génère l'infrastructure et vérifie que les frontières architecturales sont respectées. Vous pouvez voir ces principes appliqués à un projet réel dans l'étude de cas e-commerce.

Votre domaine est au centre.
HexaGlue protège ses frontières.

Voyez l'architecture hexagonale appliquée sur un projet réel ou commencez avec le tutoriel.