Infrastructure générée,
application fonctionnelle.
- Plugins JPA et REST configurés en mono-module.
- ~55 fichiers d'infrastructure générés automatiquement.
- Score final 63/100, grade D, 1 violation majeure.
Configuration HexaGlue
hexaglue.yaml à la racine du projet configure les trois plugins actifs et déclare les exclusions de classification.La configuration mono-module est plus simple : aucun
targetModule n'est nécessaire, tout le code généré atterrit dans le même module Maven.classification: exclude: - "com.acme.shop.infrastructure.**" - "com.acme.shop.exception.**"
plugins: io.hexaglue.plugin.jpa: entitySuffix: "JpaEntity" repositorySuffix: "JpaRepository" adapterSuffix: "RepositoryAdapter" generateRepositories: true generateAdapters: true generateEmbeddables: true enableAuditing: false
io.hexaglue.plugin.rest: flattenValueObjects: true generateExceptionHandler: true generateConfiguration: false
io.hexaglue.plugin.livingdoc: outputDir: "living-doc"- Pas de
targetModule: en mono-module, tout le code généré (entités JPA, repositories, adapters, controllers REST, DTOs) est produit dans le même module. HexaGlue n'a pas besoin d'instruction de routage generateExceptionHandler: true: le plugin génère un@RestControllerAdviceglobal qui couvreIllegalArgumentException, les erreurs de validation Bean et les exceptions génériques. Il remplace le handler d'exceptions manuel qu'il n'est plus nécessaire de maintenirgenerateConfiguration: false: la configuration Spring manuelle (ApplicationServiceConfig) est conservée car elle gère des dépendances inter-services. Par exemple,OrderApplicationServicedépend d'InventoryUseCases(un driving port), pas seulement de driven ports. Le générateur ne câble que les dépendances directes vers les driven ports- Exclusions :
infrastructure.**etexception.**sont explicitement exclus du périmètre DDD. HexaGlue ne classifie que les packages domaine, ports et application
Code généré automatiquement
Aucun de ces fichiers n'est écrit à la main : entités JPA, repositories, mappers, adapters, controllers REST et DTOs sont tous produits au compile-time.
Plugin JPA → src/main/java
CustomerJpaEntity, InventoryJpaEntity, OrderJpaEntity, PaymentJpaEntity, ProductJpaEntity, ShipmentJpaEntity
AddressEmbeddable, MoneyEmbeddable, QuantityEmbeddable
CustomerJpaRepository, InventoryJpaRepository, OrderJpaRepository, PaymentJpaRepository, ProductJpaRepository, ShipmentJpaRepository
CustomerMapper, InventoryMapper, OrderMapper, PaymentMapper, ProductMapper, ShipmentMapper
CustomerRepositoryAdapter, InventoryRepositoryAdapter, OrderRepositoryAdapter, PaymentRepositoryAdapter, ProductRepositoryAdapter, ShipmentRepositoryAdapter
Plugin REST → src/main/java
CustomerController, InventoryController, OrderController, PaymentController, ProductController, ShipmentController, NotificationController
Objets de transfert aplatis depuis les value objects domaine (flattenValueObjects: true)
GlobalExceptionHandler : gestionnaire global des erreurs domaine
ApplicationServiceConfig : configuration manuelle conservée avec generateConfiguration: false (dépendances inter-services)
- ~55 fichiers générés remplacent le code infrastructure manuel : 27 fichiers JPA (entités, embeddables, repositories, mappers, adapters) et environ 9 fichiers REST de base (controllers, exception handler), auxquels s'ajoutent les ~18 DTOs
- La configuration Spring reste manuelle :
generateConfiguration: falseparce queApplicationServiceConfiggère des dépendances inter-services (OrderUseCases→InventoryUseCases). Le générateur ne câble que les driven ports directs. En mono-module simple (sans dépendances inter-services),generateConfiguration: truegénère la config automatiquement - Dépendance springdoc-openapi : les controllers générés utilisent les annotations
@Operationet@Tagde Swagger. Le POM doit déclarerspringdoc-openapi-starter-webmvc-uidans une version compatible avec Spring Boot (2.4.0 pour Spring Boot 3.2.5) - Synchronisation garantie : si le domaine évolue, toute la couche infrastructure est régénérée automatiquement à la compilation suivante, sans intervention manuelle
Rapport d'audit
Rapport d'audit final après génération automatique JPA et REST, migration des identifiants et refactoring fonctionnel.
Verdict de l'audit
OrderLine.Le statut PASSED_WITH_WARNINGS reflète l'absence de violation Blocker ou Critical, avec une seule violation Major résiduelle qui ne compromet pas la conformité hexagonale.
- Statut PASSED_WITH_WARNINGS : aucune violation Blocker ou Critical. La violation
aggregate-boundarysurOrderLineest de sévérité Major : elle relève d'une limitation du classificateur (absence d'identifiant explicite), pas d'un défaut d'architecture - Grade D (score ≥ 60) : DDD Compliance (95%) et Hexagonal Architecture (100%) sont proches du maximum, mais Dependencies (0%) et Coupling (36%) plafonnent le score. Ces dimensions mesurent les cycles de dépendances et le couplage entre packages, indépendamment de la conformité hexagonale
Décomposition du score
DDD Compliance et Hexagonal Architecture sont proches du maximum. Dependencies et Coupling, qui mesurent les cycles inter-packages, concentrent les points perdus.
| Dimension | Score | Status | Delta |
|---|---|---|---|
| DDD Compliance | 95% | +95 | |
| Hexagonal Architecture | 100% | +100 | |
| Dependencies | 0% | = | |
| Coupling | 36% | +6 | |
| Cohesion | 65% | +7 | |
| TOTAL | 63.90 | ||
- DDD Compliance à 95% : la quasi-totalité des patterns tactiques DDD sont en place (6 agrégats avec identifiants typés, 7 value objects, 2 entités, 1 domain event, ports correctement séparés). Les 5% manquants correspondent à la violation
aggregate-boundarysurOrderLine - Hexagonal Architecture à 100% : les services applicatifs sont des POJOs purs, chaque port dispose d'un adaptateur généré, les couches sont isolées sans aucune dépendance croisée
- Dependencies à 0% : cette dimension pénalise les cycles de dépendances entre packages. Des cycles inter-packages subsistent dans la base de code mono-module, indépendamment de la qualité de l'architecture hexagonale
- Coupling à 36% : le couplage inter-packages est élevé car plusieurs bounded contexts partagent des types dans un mono-module. La migration vers une structure multi-module résoudrait cette dimension
- Cohesion à 65% : la concentration fonctionnelle des packages est satisfaisante mais pourrait progresser en regroupant les types par bounded context
Inventaire architectural
Il montre la répartition finale entre agrégats, entités, value objects, ports et services applicatifs.
| Composant | Nombre |
|---|---|
| Aggregate Roots | 6 |
| Entities | 2 |
| Value Objects | 7 |
| Identifiers | 6 |
| Domain Events | 1 |
| Application Services | 7 |
| Driving Ports | 7 |
| Driven Ports | 8 |
- 6 Aggregate Roots : Customer, Inventory, Order, Payment, Product, Shipment. Chaque entité métier principale est un agrégat avec identifiant typé UUID et frontières définies
- 7 Value Objects : Email, Address, Money, OrderStatus, Quantity, PaymentStatus, Category. Le domaine exprime ses concepts sans primitives exposées
- 1 Domain Event :
OrderPlacedEvent. Premier pas vers un modèle événementiel ; les abonnés potentiels (notification, stock) sont déjà câblés via les driven ports - 7 Driving Ports et 8 Driven Ports : l'hexagone est complet. Chaque driven port dispose d'un adaptateur généré par HexaGlue ; les driving ports exposent les use cases aux contrôleurs REST générés
Violations détectées
Elle est liée à une limitation du classificateur sur
OrderLine, et non à un défaut d'architecture.| Contrainte | Nb | Sév. |
|---|---|---|
| ddd:aggregate-boundary | 1 | MAJ |
ddd:aggregate-boundary(1 MAJOR) :OrderLinen'a pas d'identifiant explicite, donc le classificateur ne le reconnaît pas comme Entity membre de l'agrégatOrder. Il est perçu comme accessible hors de la frontière de l'agrégat. Ajouter unOrderLineIdrésoudrait cette violation- Pas de violation Blocker ou Critical : toutes les violations d'isolation (
layer-isolation), de couverture des ports (port-coverage) et de pureté applicative (application-purity) ont été résolues au fil des étapes précédentes
Métriques de qualité
La pureté du domaine atteint 100% et la complexité reste très basse, mais le boilerplate technique est élevé en raison du volume de code généré.
| Métrique | Valeur | Status |
|---|---|---|
| Domain purity | 100.00% | |
| Domain coverage | 50.00% | |
| Aggregate boundary | 0.00% | |
| Code boilerplate | 80.20% | |
| Code complexity | 1.12 | |
| Adapter independence | 100.00% |
domain.purityà 100% : le domaine ne contient aucune dépendance technique. Les annotations JPA, Spring et MapStruct sont toutes dans la couche infrastructure généréedomain.coverageà 50% : la moitié des types du projet sont des types domaine classifiés. L'autre moitié correspond aux types infrastructure générés, ce qui est attenduaggregate.boundaryà 0% : entièrement lié à la limitation du classificateur surOrderLine(absence d'identifiant explicite). Pas un défaut d'architecturecode.boilerplateà 80.20% (CRITICAL) : ce ratio élevé reflète le volume de code infrastructure généré par HexaGlue. Les entités JPA, mappers, repositories et DTOs sont techniques par nature ; le seuil de 50% est conçu pour le code domaine puradapter.independenceà 100% : les adaptateurs générés ne dépendent pas les uns des autres et respectent le principe d'isolation des couches externes
Stabilité des packages
Elle révèle les packages en Zone of Pain (stables mais concrets) et ceux bien positionnés sur la Main Sequence.
| Package | Ca | Ce | I | A | D | Zone |
|---|---|---|---|---|---|---|
| com.acme.shop.application | 0 | 28 | 1.00 | 0.00 | 0.00 | Main Sequence |
| com.acme.shop.domain.order | 40 | 2 | 0.05 | 0.00 | 0.95 | Zone of Pain |
| com.acme.shop.domain.product | 22 | 1 | 0.04 | 0.00 | 0.96 | Zone of Pain |
| com.acme.shop.infrastructure.persistence | 0 | 28 | 1.00 | 0.34 | 0.34 | Main Sequence |
| com.acme.shop.ports.in | 15 | 13 | 0.46 | 1.00 | 0.46 | Main Sequence |
| com.acme.shop.ports.out | 16 | 17 | 0.52 | 1.00 | 0.52 | Main Sequence |
domain.orderetdomain.producten Zone of Pain : ces packages sont très stables (40 et 22 dépendances afférentes, presque aucune dépendance efférente) mais totalement concrets (A=0.00). Position attendue pour des modèles domaine denses en mono-module ; la résoudre implique de découper par bounded context ou de migrer vers un multi-moduleports.inetports.outsur la Main Sequence : les ports sont abstraits (A=1.00) et correctement positionnés. Ils constituent l'hexagone au sens strict, stables et abstraitsapplicationetinfrastructure.persistence: ces packages sont instables (I=1.00 et Ca=0). Aucun package ne dépend d'eux. C'est la position correcte pour les couches externes : elles dépendent du domaine et ne sont pas dépendées
Plan de remédiation
Elle concerne la violation
aggregate-boundary sur OrderLine, estimée à 2 jours de travail manuel.| Contrainte | Violations | Manuel | Avec HexaGlue |
|---|---|---|---|
● ddd:aggregate:boundary Route all access to internal entities through the aggregate root | 1 | 2 j | 2 j |
| Total | 1 | 2 j 1 000 € | 2 j 1 000 € |
- 2 jours de remédiation pour corriger la violation
aggregate-boundary: ajouter unOrderLineIdtypé permet au classificateur de reconnaîtreOrderLinecomme Entity membre de l'agrégatOrder, et de router tous les accès via la racine d'agrégat - 0 jour HexaGlue : cette correction est purement domaine. HexaGlue ne peut pas corriger automatiquement une frontière d'agrégat manquante. Une fois la correction appliquée, les adaptateurs générés s'adapteront automatiquement
Avant vs Après
Les chiffres montrent ce que la migration apporte concrètement : suppression de la dette technique, ajout des patterns DDD, et génération automatique de l'infrastructure.
| Critère | Legacy | Hexagonal |
|---|---|---|
| Classes manuelles | 50 | 44 |
| Infrastructure générée | 0 | ~55 (JPA + REST) |
| JPA dans le domaine | 9 classes | 0 |
| Value Objects | 0 | 7 |
| Identifiants typés | 0 | 6 |
| Domain Events | 0 | 1 |
| Driving Ports | 0 | 7 |
| Driven Ports | 0 | 8 |
| Score architectural | N/A | 63/100 |
Enseignements
Le domaine doit être pur
La suppression des 9 classes JPA du domaine est le point d'inflexion. Tant que le domaine contient du code technique, HexaGlue ne peut pas générer l'infrastructure correctement.
Mono-module : configuration adaptée
Sans dépendance inter-services, generateConfiguration: true génère le câblage automatiquement. Avec des dépendances inter-services (ici OrderUseCases → InventoryUseCases), generateConfiguration: false et la config manuelle est conservée.
Convention reconstitute()
La factory method reconstitute() est indispensable pour que les mappers générés puissent restaurer les agrégats depuis la persistance, sans compromettre l'encapsulation du domaine.
Migration incrémentale
Chaque étape compile. Les rapports d'audit guident l'étape suivante. Pas de Big Bang : le score progresse de 13 à 63/100 de manière incrémentale.
Le score est un indicateur fidèle
63/100 avec 1 violation signale une architecture hexagonale bien structurée. Dependencies (0%) et Coupling (36%) indiquent le prochain axe d'amélioration : la séparation des bounded contexts.
Infrastructure sans code manuel
~55 fichiers générés remplacent l'infrastructure manuelle. Le coût de l'architecture hexagonale est absorbé par la génération automatique : entités JPA, repositories, mappers, adapters, controllers et DTOs.
Une architecture hexagonale opérationnelle.
Un socle solide pour aller plus loin.
L'application e-commerce mono-module est passée de 13/100 à 63/100, avec Hexagonal Architecture à 100% et ~55 fichiers générés automatiquement.