Retour
Architecture hexagonale en pratique : retour d'expérience sur 3 projets
Rappel : l’architecture hexagonale, ou Ports & Adapters
L’architecture hexagonale, formalisée par Alistair Cockburn en 2005, repose sur un principe fondamental : isoler le coeur métier de l’application de ses dépendances techniques. Le nom “hexagonal” est en réalité secondaire — Cockburn lui-même préfère le terme “Ports & Adapters” qui décrit plus fidèlement le mécanisme.
Le domaine (ou coeur métier) contient les règles de gestion, les entités, les value objects et les services métier. Il ne dépend d’aucune librairie externe, d’aucun framework, d’aucune base de données. Il est pur, testable unitairement en isolation, et représente la valeur durable de l’application.
Les ports sont des interfaces définies par le domaine. On distingue les ports primaires (driving ports), qui exposent les cas d’usage du domaine au monde extérieur (API REST, CLI, événements), et les ports secondaires (driven ports), que le domaine utilise pour accéder à des ressources externes (base de données, API tierces, système de fichiers, messagerie).
Les adapters sont les implémentations concrètes de ces ports. Un adapter primaire est, par exemple, un contrôleur REST Spring Boot qui reçoit une requête HTTP et appelle un port primaire du domaine. Un adapter secondaire est, par exemple, un repository JPA qui implémente un port secondaire de persistance.
La règle de dépendance est stricte : les dépendances pointent toujours vers l’intérieur, c’est-à-dire vers le domaine. Le domaine ne connaît jamais ses adapters, uniquement les ports (interfaces) qu’il définit. Cette inversion de dépendance est la clé de l’architecture.
En théorie, c’est élégant. En pratique, c’est là que les choses deviennent intéressantes. Chez Nobori, nous avons mis en oeuvre cette architecture sur de nombreux projets, avec des résultats contrastés. Voici trois retours d’expérience qui illustrent les réussites, les difficultés et les leçons apprises.
Projet 1 : Microservice de facturation en Java — un succès
Contexte
Un client du secteur de l’énergie nous a confié le développement d’un microservice de facturation. Le service devait calculer les factures à partir de données de consommation, appliquer les règles tarifaires complexes (tarifs réglementés, offres de marché, heures pleines/creuses, taxes locales), et produire les documents de facturation.
La complexité était clairement dans le domaine métier : des dizaines de règles tarifaires, des cas de bord réglementaires, des calculs de prorata temporis. L’infrastructure technique, en revanche, était relativement simple : une API REST en entrée, une base PostgreSQL et un système de files (RabbitMQ) en sortie.
Mise en oeuvre
Nous avons structuré le projet en 4 modules Maven :
| Module | Contenu | Dépendances |
|---|---|---|
domain | Entités, Value Objects, Ports, Services métier | Aucune (Java pur) |
application | Use cases, orchestration, DTOs application | domain uniquement |
infrastructure | Adapters JPA, REST controllers, RabbitMQ publisher | domain, application, Spring Boot, Hibernate |
bootstrap | Configuration, main, injection de dépendances | Tous les modules |
Le module domain représentait 60% du code et contenait zéro import Spring. Les entités métier (Facture, LigneFacture, GrilleTarifaire, PeriodeConsommation) étaient des objets Java riches avec une logique de validation intégrée, conformément aux principes du Domain-Driven Design.
Les tests unitaires du domaine couvraient 94% du code métier et s’exécutaient en 800 millisecondes sans aucun framework de mock ni conteneur Docker. C’est le bénéfice le plus immédiat de l’architecture hexagonale : la testabilité du coeur métier.
Résultats
Le projet a été livré en 4 mois avec une équipe de 3 développeurs. À 18 mois de production, le bilan est très positif.
Points forts : le domaine n’a jamais été impacté par les évolutions techniques. Quand le client a voulu migrer de RabbitMQ vers Kafka, seul l’adapter de messagerie a été modifié. Les 400+ tests unitaires du domaine n’ont pas bougé. L’onboarding de nouveaux développeurs est rapide : ils comprennent les règles métier en lisant le domaine, sans être noyés dans le framework.
Investissement : environ 15 à 20% de temps supplémentaire par rapport à une approche classique (entités JPA directement exposées), principalement sur la mise en place initiale de la structure et les mappings entre couches.
Lecon
L’architecture hexagonale brille lorsque le domaine métier est complexe et stable dans le temps, alors que l’infrastructure technique est susceptible d’évoluer. Le ratio investissement/bénéfice est clairement favorable dans ce cas.
Projet 2 : API Python de recommandation — un résultat mitigé
Contexte
Pour un client dans le secteur du retail, nous avons développé une API de recommandation produit. Le service recevait un contexte utilisateur (historique d’achats, navigation récente, panier en cours) et retournait une liste de produits recommandés, en s’appuyant sur un modèle de machine learning pré-entraîné.
La complexité résidait ici non pas dans le domaine métier (relativement simple : récupérer le contexte, appeler le modèle, formater les résultats) mais dans l’infrastructure technique : intégration avec un modèle ML servé via TensorFlow Serving, cache Redis pour les features utilisateur, base de données de catalogue produit, A/B testing des modèles.
Mise en oeuvre
Nous avons appliqué l’architecture hexagonale en Python avec FastAPI, en suivant une structure similaire à celle du projet Java : un package domain pur, des ports définis comme des classes abstraites Python (ABC), et des adapters pour Redis, le catalogue produit et TensorFlow Serving.
Difficultés rencontrées
Le domaine était trop mince. Le coeur métier se résumait essentiellement à : “récupérer les features, appeler le modèle, filtrer les résultats”. La logique tenait en une centaine de lignes. En revanche, nous avions des centaines de lignes de mapping entre les couches, des interfaces abstraites pour des implémentations qui ne changeraient probablement jamais, et une complexité structurelle disproportionnée.
L’écosystème Python résiste à l’hexagonal. Les conventions Python (duck typing, modules comme namespaces, dataclasses plutôt qu’objets riches) ne se prêtent pas naturellement à la rigueur structurelle de l’hexagonal. Les développeurs Python de l’équipe trouvaient le code “sur-architecturé” et “non-idiomatique”. Les revues de code généraient des débats récurrents sur la structure plutôt que sur la logique.
Le coût de maintenance a dépassé le bénéfice. Chaque modification, même mineure, nécessitait de toucher 3 à 4 fichiers (port, adapter, mapping, use case) là où un changement dans un seul fichier aurait suffi. Pour un service dont le domaine métier est simple et dont la complexité est technique, ce surcoût n’était pas justifié.
Correction de trajectoire
À la demande de l’équipe, nous avons simplifié l’architecture au bout de 3 mois en adoptant une Clean Architecture allégée : une couche service (logique métier et orchestration), une couche repository (accès données, abstrait par des interfaces simples), et les contrôleurs FastAPI. Le code est passé de 4 200 lignes à 2 800 lignes, la vitesse de développement a augmenté de 30%, et les développeurs étaient plus sereins.
Lecon
L’architecture hexagonale n’est pas universelle. Quand le domaine métier est mince et que la valeur réside dans l’intégration technique, une architecture en couches classique ou une clean architecture allégée est souvent plus adaptée. L’architecture doit servir le projet, pas l’inverse.
Projet 3 : Monolithe modulaire pour un ERP logistique — un succès
Contexte
Un client du secteur logistique souhaitait réécrire son ERP interne, un monolithe vieillissant en PHP, en Java avec Spring Boot. Le périmètre fonctionnel était vaste : gestion des entrepôts, planification des tournées, suivi des colis, facturation, relation client.
L’option microservices a été écartée après analyse : l’équipe de 8 développeurs n’avait pas la maturité opérationnelle pour gérer un écosystème distribué, et les contraintes transactionnelles entre les modules (une expédition touche l’entrepôt, la tournée, le colis et la facturation) rendaient la décomposition en services indépendants artificielle.
Mise en oeuvre
Nous avons opté pour un monolithe modulaire avec une architecture hexagonale appliquée à chaque module. Chaque module (Entrepot, Tournee, Colis, Facturation, Client) possédait sa propre structure hexagonale interne et exposait un port public (une interface Java) que les autres modules pouvaient consommer.
La structure de packages suivait un pattern strict :
com.client.erp
+-- entrepot
| +-- domain (entités, ports, services)
| +-- application (use cases)
| +-- infrastructure (adapters)
| +-- api (port public du module)
+-- tournee
| +-- domain
| +-- ...
+-- facturation
+-- domain
+-- ...
Les règles d’architecture étaient vérifiées automatiquement via ArchUnit :
- Le package
domainne peut importer aucun packageinfrastructure - Un module ne peut accéder à un autre module que via son package
api - Aucune dépendance circulaire entre modules
Résultats
Le projet a été livré en 10 mois avec une équipe de 8 développeurs. Le bilan à 12 mois est excellent.
Isolation effective : chaque module peut évoluer indépendamment tant qu’il respecte le contrat de son port public. L’équipe Facturation a pu refondre complètement son modèle de calcul sans impacter les autres modules. Les tests de régression se limitent au module modifié et aux tests d’intégration inter-modules.
Migration progressive : la structure modulaire facilite une éventuelle future extraction en microservices. Si le module Facturation doit un jour devenir un service indépendant (pour des raisons de scalabilité ou d’organisation), les interfaces sont déjà définies, et le couplage est minimal.
Simplicité opérationnelle : un seul artifact à déployer, une seule base de données (avec des schémas séparés par module), pas de réseau distribué à gérer. L’équipe peut se concentrer sur le développement fonctionnel plutôt que sur l’infrastructure.
Lecon
L’architecture hexagonale couplée au monolithe modulaire est un choix excellent pour les équipes qui veulent la rigueur architecturale des microservices sans la complexité opérationnelle du distribué. C’est une architecture qui vieillit bien et qui préserve les options pour l’avenir.
Quand l’architecture hexagonale vaut-elle le coup ?
Après ces trois expériences, voici notre grille de décision.
| Critère | Hexagonal recommandé | Hexagonal déconseillé |
|---|---|---|
| Complexité du domaine | Élevée (règles métier riches) | Faible (CRUD, pass-through) |
| Durée de vie prévue | Longue (> 3 ans) | Courte (PoC, MVP jetable) |
| Taille de l’équipe | Moyenne à grande (> 4 devs) | Petite (1-2 devs) |
| Évolutivité technique attendue | Forte (changement de framework, de BDD) | Faible (stack figée) |
| Écosystème / langage | Java, Kotlin, C#, TypeScript | Python, Go (idiomes différents) |
| Besoins de testabilité | Élevés (domaine critique) | Standards |
Les pièges courants
Le mapping excessif : chaque couche a ses propres objets (entité domaine, entité JPA, DTO API, DTO application). Le mapping entre ces objets peut devenir un fardeau. Notre règle : n’introduire un objet de mapping que lorsqu’il y a une réelle différence de structure entre les couches. Un mappage mécanique 1:1 est un signal de sur-ingénierie.
Le domaine anémique : des entités qui ne sont que des conteneurs de données (getters/setters) avec toute la logique dans les services. C’est de l’architecture hexagonale de facade, sans les bénéfices. Le domaine doit contenir de la logique métier riche : validation, calcul, transitions d’état.
L’obsession de la pureté : interdire toute annotation dans le domaine, même un simple @NotNull de Bean Validation. Le pragmatisme doit primer. Si une annotation ne crée pas de couplage fort avec un framework et améliore la lisibilité, elle est acceptable.
Négliger ArchUnit : sans garde-fous automatisés, les développeurs finissent par violer les règles architecturales sous la pression des délais. ArchUnit (Java), NetArchTest (.NET) ou les conventions de modules stricts (Kotlin/Gradle) sont indispensables pour maintenir l’intégrité architecturale dans la durée.
Conclusion
L’architecture hexagonale n’est ni une solution miracle ni un anti-pattern. C’est un outil puissant qui délivre des bénéfices considérables lorsqu’il est appliqué dans le bon contexte : un domaine métier complexe, une durée de vie longue, et une équipe de taille suffisante pour amortir l’investissement structurel.
Notre recommandation : commencez par analyser la complexité de votre domaine métier. Si elle est élevée et durable, l’hexagonal est un excellent choix. Si elle est faible ou transitoire, une architecture en couches classique, bien structurée, sera plus pragmatique et tout aussi maintenable.
L’architecture est un moyen, pas une fin. La meilleure architecture est celle qui permet à votre équipe de livrer de la valeur métier rapidement, durablement et sereinement.
Nobori accompagne les équipes dans le choix et l’implémentation de leurs architectures logicielles et SI. Découvrir notre offre Architecture & Modernisation SI.
Pour aller plus loin
- Découvrez notre expertise Architecture et nos approches de conception logicielle
- Consultez notre article sur le Platform Engineering pour comprendre comment industrialiser les Golden Paths architecturaux
- Explorez nos missions en Platform Engineering pour voir comment l’architecture s’intègre dans une chaîne de livraison continue
Ce sujet vous concerne ?
Découvrez comment notre expertise en Architecture SI peut accélérer votre projet.
Découvrir l'expertise
Newsletter
Restez informé
Analyses Cloud, Data & IA — 1 email par mois, pas plus.
Inscription confirmée
Merci ! Vous recevrez notre prochaine analyse directement dans votre boîte mail.


