Aller au contenu
Riadh Mnasri
← Retour au blog
3 min de lecture

Frontière hexagonale en Kotlin : modules Gradle plutôt que des dossiers

L'article sur l'architecture hexagonale en pratique le disait déjà : la question qui compte n'est pas « est-ce dans le bon dossier » mais « le domaine compile-t-il seul si on supprime les adapters ». Un dossier domain/ ne répond jamais vraiment à cette question, il ne fait que suggérer une intention. La seule façon de la transformer en garantie, c'est de la faire vérifier par le compilateur, pas par la relecture de code.

Ce qu'un dossier ne peut pas empêcher#

Rien, dans un projet Gradle à module unique, n'empêche un fichier du dossier domain/ d'importer org.springframework.stereotype.Component ou jakarta.persistence.Entity. Le code compile, les tests passent, et la frontière hexagonale existe uniquement dans la tête de qui a écrit le code et de qui l'a relu. C'est une discipline, pas une contrainte : elle tient tant que personne ne l'oublie sous la pression d'une deadline.

La frontière comme dépendance Gradle, pas comme convention de dossier#

Un module Gradle dédié au domaine, sans aucune dépendance vers Spring, Jakarta ou un driver de base de données, transforme cette discipline en contrainte de compilation. Si un fichier du module domain importe une classe Spring, le build échoue avec une erreur de résolution, pas une remarque en revue de code.

kotlin
// settings.gradle.kts
include("domain", "application", "infrastructure", "presentation")
kotlin
// domain/build.gradle.kts
dependencies {
    // rien d'autre que le langage lui-même
    implementation(kotlin("stdlib"))
}
kotlin
// application/build.gradle.kts
dependencies {
    implementation(project(":domain"))
    // toujours aucune dépendance framework ici
}
kotlin
// infrastructure/build.gradle.kts
dependencies {
    implementation(project(":domain"))
    implementation(project(":application"))
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
}

Le graphe de dépendances que Gradle refuse de laisser inverser#

infrastructure -> application -> domain
presentation   -> application

domain ne dépend jamais d'un module en amont : Gradle n'offre
littéralement pas de chemin pour déclarer l'inverse sans créer
un cycle, que le build rejette de lui-même.

Un import qui remonterait de domain vers infrastructure ne produit pas une classe mal rangée, il produit un cycle de modules que Gradle refuse de résoudre. La violation devient une erreur de build, plus tôt et plus systématiquement qu'une remarque de revue de code qui dépend de la vigilance de qui relit.

Ce que ça coûte#

Cette rigueur a un prix : plus de fichiers build.gradle.kts à maintenir, un temps de configuration Gradle légèrement plus long, et une gymnastique supplémentaire chaque fois qu'un type doit franchir une frontière de module (des DTOs de mapping en plus, là où un module unique laissait parfois passer une entité de domaine directement jusqu'au contrôleur). Sur un projet de quelques semaines, ce coût dépasse largement le bénéfice. Sur un projet fait pour vivre des années, avec plusieurs contributeurs qui n'auront pas tous lu la même documentation d'architecture, faire vérifier la frontière par le compilateur plutôt que par la mémoire collective de l'équipe devient rentable dès la première fois où ça évite une régression silencieuse.

Où appliquer ça#

Un projet comme MissionMatch, où la frontière hexagonale est aujourd'hui une question de dossiers et de discipline d'équipe, est un candidat naturel pour ce genre de renforcement le jour où le nombre de contributeurs augmente. Tant que le projet reste piloté par une seule personne qui connaît chaque frontière par cœur, le dossier suffit. Dès qu'il ne l'est plus, faire vérifier la frontière par le compilateur devient plus fiable que de compter sur la mémoire de l'équipe.