<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: BANGA</title>
    <description>The latest articles on DEV Community by BANGA (@bangaromaric).</description>
    <link>https://dev.to/bangaromaric</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F972716%2F13c5b0a1-18a1-4aa1-8742-516eabd56057.jpeg</url>
      <title>DEV Community: BANGA</title>
      <link>https://dev.to/bangaromaric</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bangaromaric"/>
    <language>en</language>
    <item>
      <title>MOUSSAVOU apprend DDD : le guide pratique du dev qui veut écrire du code qui tient</title>
      <dc:creator>BANGA</dc:creator>
      <pubDate>Sun, 17 May 2026 09:02:48 +0000</pubDate>
      <link>https://dev.to/bangaromaric/moussavou-apprend-ddd-le-guide-pratique-du-dev-qui-veut-ecrire-du-code-qui-tient-lk</link>
      <guid>https://dev.to/bangaromaric/moussavou-apprend-ddd-le-guide-pratique-du-dev-qui-veut-ecrire-du-code-qui-tient-lk</guid>
      <description>&lt;h3&gt;
  
  
  Comment DDD, l'architecture hexagonale et Spring Modulith te permettent d'utiliser Claude, ChatGPT et Copilot sans laisser l'IA générer un monstre — étude de cas MboloPay, mini mobile money open source en français
&lt;/h3&gt;




&lt;p&gt;Il est &lt;strong&gt;23h17 à Libreville&lt;/strong&gt;. MOUSSAVOU relit pour la troisième fois la PR que son lead vient de rejeter. PayApp, la fintech où elle a été embauchée il y a dix-huit mois, doit livrer demain matin l'intégration avec Orange Money. &lt;strong&gt;Pourtant elle a fait les choses bien — elle a demandé à Claude de l'aider, copié les meilleures réponses Stack Overflow, ajusté avec Copilot.&lt;/strong&gt; Et un simple changement — accepter aussi les nouveaux numéros à 9 chiffres en plus des 10 actuels — fait quand même exploser &lt;strong&gt;47 tests dans 12 fichiers&lt;/strong&gt; qui n'ont rien à voir.&lt;/p&gt;

&lt;p&gt;Validation Spring dans le &lt;code&gt;AbonneController&lt;/code&gt; (générée par ChatGPT il y a six mois). Regex copiée-collée dans trois services (Copilot a complété, elle a dit oui). Méthode &lt;code&gt;setNumeroTelephone()&lt;/code&gt; qui contrôle parfois (un Stack Overflow qu'elle a oublié de finir d'adapter). Le numéro circule en &lt;code&gt;String&lt;/code&gt; partout dans le code. &lt;strong&gt;L'IA a livré 1 000 lignes en trois mois. Personne — pas même Claude — ne sait plus &lt;em&gt;où&lt;/em&gt; poser cette satanée nouvelle règle.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si cette scène te parle — si tu as déjà ouvert un &lt;code&gt;UserService.java&lt;/code&gt; de 800 lignes dans une fintech à Dakar, Lomé ou Yaoundé — ne ferme pas cet onglet. Dans 15 minutes, tu vas connaître trois patterns qui auraient évité à MOUSSAVOU son insomnie : &lt;strong&gt;DDD&lt;/strong&gt;, &lt;strong&gt;Architecture Hexagonale&lt;/strong&gt;, &lt;strong&gt;Spring Modulith&lt;/strong&gt;. Tu vas les voir en action sur un projet open source ancré dans notre écosystème : &lt;strong&gt;MboloPay&lt;/strong&gt; (&lt;em&gt;mbolo&lt;/em&gt; veut dire "bonjour" en Fang, langue gabonaise), un mini service de mobile money écrit en français, avec Airtel Money et Moov Money comme opérateurs, et une démo live qui te laisse explorer l'architecture &lt;strong&gt;en mouvement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Spoiler : ton problème n'est pas que tu utilises l'IA. &lt;strong&gt;Ton problème est que tu lui fais confiance pour des choses qu'elle ne peut pas faire.&lt;/strong&gt; L'IA exécute. C'est toi qui dois architecter.&lt;/p&gt;

&lt;p&gt;Pas de théorie poussiéreuse. Du concret. On commence par comprendre pourquoi le code d'MOUSSAVOU s'est emmêlé — parce que tant qu'on ne nomme pas le problème, on tourne en rond.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Pourquoi un Spring Boot classique finit en spaghetti
&lt;/h2&gt;

&lt;p&gt;Spring Boot t'apprend trois sigles dès la première semaine : &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;@Service&lt;/code&gt;, &lt;code&gt;@Repository&lt;/code&gt;. Trois couches techniques empilées comme un sandwich. Sur un CRUD simple, ça marche. Sur l'intégration Orange Money de MOUSSAVOU, ça craque.&lt;/p&gt;

&lt;p&gt;Pourquoi ? Parce que ces couches &lt;strong&gt;ne disent rien du métier&lt;/strong&gt;. Quand MOUSSAVOU veut savoir &lt;em&gt;où&lt;/em&gt; mettre la règle « un numéro Airtel commence par &lt;code&gt;+24107&lt;/code&gt; et un Moov par &lt;code&gt;+24106&lt;/code&gt; », elle a quatre candidats légitimes :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Le controller, via &lt;code&gt;@Valid&lt;/code&gt; sur le DTO.&lt;/li&gt;
&lt;li&gt;Le service, via une vérification manuelle dans &lt;code&gt;creerAbonne()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;L'entité JPA, via &lt;code&gt;@Pattern&lt;/code&gt; Bean Validation.&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;@PrePersist&lt;/code&gt; listener Hibernate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Réponse classique dans la vraie vie : &lt;strong&gt;les quatre, en même temps, sans coordination&lt;/strong&gt;. C'est ce qu'on appelle l'&lt;strong&gt;anemic domain model&lt;/strong&gt; (modèle de domaine anémique) : tes entités sont des sacs de getters/setters sans comportement. La logique métier se disperse comme du sucre versé sur une table. Et le jour où la règle change, tu passes 6 heures à chasser les copies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Et l'IA dans tout ça ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En 2026, ce que je viens de décrire n'arrive plus &lt;em&gt;malgré&lt;/em&gt; l'IA — ça arrive &lt;strong&gt;à cause&lt;/strong&gt; d'elle. ChatGPT, Claude, Copilot sont incroyables pour produire du code qui compile. Mais si tu ne sais pas &lt;strong&gt;quelle architecture&lt;/strong&gt; tu vises, l'IA te livre 800 lignes de &lt;code&gt;UserService&lt;/code&gt; aussi vite que tu peux les copier-coller. L'IA n'a aucune opinion sur l'architecture par défaut — elle reproduit le pattern le plus fréquent dans son corpus d'entraînement, qui est précisément le sandwich &lt;code&gt;@RestController&lt;/code&gt; / &lt;code&gt;@Service&lt;/code&gt; / &lt;code&gt;@Repository&lt;/code&gt; que tu as vu sur 10 000 tutos.&lt;/p&gt;

&lt;p&gt;Plus vite, donc. Mais plus profondément en spaghetti 🍝&lt;/p&gt;

&lt;p&gt;💡 L'astuce ? &lt;strong&gt;Ne plus penser en couches techniques. Penser en &lt;em&gt;domaines métier&lt;/em&gt;.&lt;/strong&gt; Et faire en sorte que la règle « un numéro Orange Money valide ressemble à ça » n'ait qu'&lt;strong&gt;un seul endroit&lt;/strong&gt; où vivre. Spoiler : ce n'est pas dans le controller.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;C'est exactement ce que DDD propose. Allons voir.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. DDD — Modéliser le métier, pas la technique
&lt;/h2&gt;

&lt;p&gt;DDD (&lt;em&gt;Domain-Driven Design&lt;/em&gt;, conception pilotée par le domaine) part d'une idée simple : ton code doit refléter le métier, pas la mécanique technique. Quatre concepts à connaître. Quatre analogies pour les ancrer.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Bounded Context = département d'une entreprise
&lt;/h3&gt;

&lt;p&gt;Imagine une grande boîte de distribution à Abidjan. Le service &lt;strong&gt;commercial&lt;/strong&gt; parle de « client » : un prospect qu'il faut convaincre. Le service &lt;strong&gt;comptabilité&lt;/strong&gt; parle de « client » aussi : un compte qu'il faut facturer. Le service &lt;strong&gt;livraison&lt;/strong&gt; ? Encore « client » : une adresse où amener le colis. Trois départements, trois définitions différentes du même mot. Et c'est très bien comme ça.&lt;/p&gt;

&lt;p&gt;Un &lt;strong&gt;Bounded Context&lt;/strong&gt; (BC, contexte délimité) c'est exactement ça : un périmètre où un vocabulaire métier est cohérent. À l'intérieur du BC, « client » veut dire UNE chose. À la frontière, on traduit.&lt;/p&gt;

&lt;p&gt;MboloPay a &lt;strong&gt;trois bounded contexts&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;identite&lt;/code&gt; — connaît les abonnés. Pour lui, un abonné a un nom, un numéro de téléphone gabonais, une date d'inscription.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;portefeuille&lt;/code&gt; — connaît les portefeuilles. Pour lui, un abonné est juste un identifiant qu'il transporte sans rien savoir d'autre.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shared&lt;/code&gt; — un mini module ouvert qui contient &lt;code&gt;ExceptionDomaine&lt;/code&gt;, racine de toutes les exceptions métier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chaque BC vit dans son propre paquet Java. Aucun n'importe les classes internes de l'autre. Comment ils communiquent alors ? On y vient, c'est la magie de Spring Modulith. Patience.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Aggregate = la racine et ses branches
&lt;/h3&gt;

&lt;p&gt;Dans un BC, certaines classes sont plus importantes que d'autres. Elles sont des &lt;strong&gt;Aggregate Roots&lt;/strong&gt; (racines d'agrégat) — les troncs d'arbres métier. Tu veux toucher une feuille ? Tu passes par la racine. Toujours.&lt;/p&gt;

&lt;p&gt;Dans MboloPay, &lt;code&gt;Abonne&lt;/code&gt; est un Aggregate Root du module &lt;code&gt;identite&lt;/code&gt;. &lt;code&gt;Portefeuille&lt;/code&gt; est un Aggregate Root du module &lt;code&gt;portefeuille&lt;/code&gt;. Tu ne crées &lt;strong&gt;jamais&lt;/strong&gt; un &lt;code&gt;Abonne&lt;/code&gt; avec &lt;code&gt;new Abonne()&lt;/code&gt; suivi de 12 setters. Tu utilises une factory :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@AggregateRoot&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Abonne&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... champs privés finals&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Abonne&lt;/span&gt; &lt;span class="nf"&gt;creer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NomGabonais&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;NumeroTelephoneGabonais&lt;/span&gt; &lt;span class="n"&gt;numero&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// les invariants s'appliquent ICI, à la naissance&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Abonne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AbonneId&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nouveau&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numero&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// PAS de setNom(...), PAS de setNumero(...)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pourquoi c'est révolutionnaire ? Parce qu'&lt;strong&gt;un &lt;code&gt;Abonne&lt;/code&gt;, dès qu'il existe, est valide&lt;/strong&gt;. Pas de phase « à moitié construit, j'attends d'appeler le setter suivant ». Pas de risque qu'un junior pressé oublie un &lt;code&gt;setActif(true)&lt;/code&gt;. La règle métier vit dans la factory, pas éparpillée dans 10 endroits.&lt;/p&gt;

&lt;p&gt;Tu te dis sûrement : « OK mais on perd la flexibilité des setters ». Réponse : oui. Et c'est précisément le but.&lt;/p&gt;

&lt;p&gt;Un Aggregate Root sans ses invariants, c'est comme un kiosque mobile money qui prend l'argent sans noter qui a déposé combien. Techniquement opérationnel, statistiquement catastrophique.&lt;/p&gt;

&lt;p&gt;Petit test IA : demande maintenant à Claude ou ChatGPT &lt;em&gt;« crée une classe Java Abonne avec nom et numéro de téléphone »&lt;/em&gt;. Tu reçois 90 % du temps : &lt;code&gt;class Abonne { private String nom; public void setNom(...) }&lt;/code&gt;. Modèle anémique. Setters publics. Aucun invariant.&lt;/p&gt;

&lt;p&gt;Reformule : &lt;em&gt;« crée un Aggregate Root jMolecules pour Abonne, avec factory &lt;code&gt;creer()&lt;/code&gt;, champs finals, et invariants appliqués à la construction »&lt;/em&gt;. L'IA te génère exactement ce qu'on vient de voir, en 10 secondes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La différence n'est pas dans l'IA. Elle est dans ton vocabulaire.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Value Object = identité par la valeur, pas par la référence
&lt;/h3&gt;

&lt;p&gt;Maintenant le vrai changement de mentalité — celui qui va régler une bonne partie de l'insomnie de MOUSSAVOU.&lt;/p&gt;

&lt;p&gt;Un &lt;strong&gt;Value Object&lt;/strong&gt; (VO) est une classe qui représente une &lt;em&gt;valeur&lt;/em&gt;, pas une &lt;em&gt;chose&lt;/em&gt;. Un billet de 5000 FCFA et un autre billet de 5000 FCFA sont &lt;strong&gt;interchangeables&lt;/strong&gt; : tu te fiches de leur numéro de série, ce qui compte c'est leur valeur. Donc &lt;code&gt;Argent(5000)&lt;/code&gt; est égal à &lt;code&gt;Argent(5000)&lt;/code&gt;, peu importe quel objet Java se cache derrière.&lt;/p&gt;

&lt;p&gt;MboloPay a une foule de VOs. Les plus parlants :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AbonneId&lt;/code&gt; — encapsule un &lt;code&gt;UUID&lt;/code&gt;. Pas un &lt;code&gt;String&lt;/code&gt;. Un &lt;code&gt;AbonneId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PortefeuilleId&lt;/code&gt; — pareil mais distinct. Tu ne peux pas confondre les deux.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Argent&lt;/code&gt; — un montant en FCFA. Validation à la construction : pas de montant négatif.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NumeroTelephoneGabonais&lt;/code&gt; — un numéro qui &lt;em&gt;garantit&lt;/em&gt; le format E.164 gabonais et l'opérateur (Airtel ou Moov).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NomGabonais&lt;/code&gt; — un nom non vide, capitalisé, sans caractères exotiques.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voici l'idée de &lt;code&gt;NumeroTelephoneGabonais&lt;/code&gt; (extrait simplifié, vérifie le vrai code &lt;a href="https://github.com/bangaromaric/mbolopay/tree/main/src/main/java/ga/banga/mbolopay/identite/domain/model/vo/NumeroTelephoneGabonais.java" rel="noopener noreferrer"&gt;sur GitHub&lt;/a&gt;) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ValueObject&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;NumeroTelephoneGabonais&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;valeur&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OperateurMobile&lt;/span&gt; &lt;span class="n"&gt;operateur&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;NumeroTelephoneGabonais&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requireNonNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valeur&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;valeur&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\+241[067]\\d{7}"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NumeroNonAutoriseException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valeur&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// operateur déduit du préfixe Airtel/Moov&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maintenant regarde cette signature de méthode :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;transferer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AbonneId&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AbonneId&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Argent&lt;/span&gt; &lt;span class="n"&gt;montant&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tu peux ? Tu &lt;strong&gt;ne peux pas&lt;/strong&gt; appeler &lt;code&gt;transferer(montant, from, to)&lt;/code&gt; par erreur. Le compilateur Java refuse. Avec des &lt;code&gt;String&lt;/code&gt; et &lt;code&gt;long&lt;/code&gt; partout, tu aurais pu envoyer 1000 FCFA à un numéro de téléphone, ou pire — un identifiant client à la place du montant — et te demander pourquoi la prod est en feu un vendredi soir. bolooooh &lt;/p&gt;

&lt;p&gt;C'est ce qu'on appelle le &lt;strong&gt;Types Driven Development&lt;/strong&gt; : &lt;em&gt;les erreurs métier deviennent des erreurs de compilation&lt;/em&gt;. MOUSSAVOU, qui peste contre ses 47 tests cassés, aurait préféré que ces 47 tests soient remplacés par UN compilateur qui dit non. C'est exactement ce que les VOs offrent.&lt;/p&gt;

&lt;p&gt;Et c'est ICI que ça devient intéressant avec l'IA. Une fois que tu connais les VOs, tu peux dire à Claude &lt;em&gt;« refactore cette classe pour remplacer chaque &lt;code&gt;String&lt;/code&gt; par un Value Object typé »&lt;/em&gt;. Il le fait en une minute. C'était sans toi 2 jours de refactor. Avec toi qui ne connais pas les VOs, c'était 0 jour — parce que tu n'aurais jamais demandé.&lt;/p&gt;

&lt;p&gt;(Oui, je sais. Encore une analogie. C'est qu'on est dans un article pédagogique. Subis.)&lt;/p&gt;

&lt;h3&gt;
  
  
  2.4 Domain Event = annonce publique dans l'entreprise
&lt;/h3&gt;

&lt;p&gt;Reprends ton entreprise au Gabon. Le service RH embauche quelqu'un. Que se passe-t-il ?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Les badges préparent un badge.&lt;/li&gt;
&lt;li&gt;La paie ouvre un dossier salaire.&lt;/li&gt;
&lt;li&gt;L'IT crée un compte mail.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tout ça &lt;strong&gt;en réaction&lt;/strong&gt;, sans que les RH appellent chaque service à la main. Comment ? Une &lt;strong&gt;annonce publique&lt;/strong&gt; : « NDONG MENGUE a été embauchée le 15 mai. » Chaque service écoute, chacun fait son boulot. Personne ne dépend frontalement de personne.&lt;/p&gt;

&lt;p&gt;C'est un &lt;strong&gt;Domain Event&lt;/strong&gt; : un fait métier qui s'est produit, qu'on annonce, et que d'autres modules peuvent écouter.&lt;/p&gt;

&lt;p&gt;MboloPay a &lt;code&gt;EvenementAbonneCree&lt;/code&gt;, publié par &lt;code&gt;identite&lt;/code&gt; quand un abonné est créé. Le module &lt;code&gt;portefeuille&lt;/code&gt; l'écoute et crée automatiquement un portefeuille à 0 FCFA pour ce nouvel abonné.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;EvenementAbonneCree&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AbonneId&lt;/span&gt; &lt;span class="n"&gt;abonneId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;survenuLe&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trois lignes. Un record immuable. &lt;strong&gt;Aucune annotation Spring.&lt;/strong&gt; C'est un fait métier, pas un message technique. C'est l'&lt;code&gt;identite&lt;/code&gt; qui annonce, et c'est le métier qui parle.&lt;/p&gt;

&lt;p&gt;Tu vois où je veux en venir ? Le module &lt;code&gt;portefeuille&lt;/code&gt; ne connaît pas le module &lt;code&gt;identite&lt;/code&gt; au sens classique. Il connaît juste un &lt;em&gt;événement&lt;/em&gt; — un nom dans un vocabulaire commun. Demain, on peut remplacer &lt;code&gt;identite&lt;/code&gt; par un service externe sans toucher &lt;code&gt;portefeuille&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;C'est exactement la promesse de DDD : &lt;strong&gt;modulariser par le métier, pas par la technique&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Architecture Hexagonale — Le cœur et ses prises
&lt;/h2&gt;

&lt;p&gt;Maintenant qu'on a nos agrégats, nos VOs et nos events, il reste UNE question : où on les met physiquement dans le code ?&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 L'idée fondatrice
&lt;/h3&gt;

&lt;p&gt;L'&lt;strong&gt;architecture hexagonale&lt;/strong&gt; (aussi appelée &lt;em&gt;Ports &amp;amp; Adapters&lt;/em&gt;) répond : le &lt;strong&gt;domaine&lt;/strong&gt; est au centre. Tout autour, des &lt;strong&gt;adaptateurs&lt;/strong&gt; qui le connectent au monde extérieur — HTTP, base de données, message broker, UI, terminaux de paiement, tout ça.&lt;/p&gt;

&lt;p&gt;Imagine un cœur. Au centre, ton domaine métier (&lt;code&gt;Abonne&lt;/code&gt;, &lt;code&gt;Portefeuille&lt;/code&gt;, leurs règles). Tout autour, des prises électriques. Chaque prise (&lt;em&gt;adapter&lt;/em&gt;) peut être débranchée et remplacée sans toucher au cœur. Tu testes le cœur avec une fake prise (un repo en mémoire). Tu déploies en prod avec une prise JPA. Tu envisages une prise gRPC pour le mobile — tu la branches.&lt;/p&gt;

&lt;p&gt;Tu ris ? Attends de voir la règle absolue.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Ports vs Adapters
&lt;/h3&gt;

&lt;p&gt;Le domaine ne &lt;strong&gt;demande&lt;/strong&gt; pas ce dont il a besoin avec des classes concrètes. Il déclare des &lt;strong&gt;interfaces&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Port &lt;code&gt;in&lt;/code&gt;&lt;/strong&gt; : interface d'un cas d'usage. Exemple : &lt;code&gt;CreerAbonneUseCase&lt;/code&gt;. Le monde extérieur l'appelle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port &lt;code&gt;out&lt;/code&gt;&lt;/strong&gt; : interface dont le domaine a besoin pour fonctionner. Exemple : &lt;code&gt;DepotAbonne&lt;/code&gt; (un repository), &lt;code&gt;PublieurEvenements&lt;/code&gt; (un publisher).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le domaine ne sait PAS qui implémente ces interfaces. Il dit « j'ai besoin de sauvegarder un Abonne ». C'est l'infrastructure qui répond : « OK, moi je le sauvegarde en JPA » (ou en mémoire pour les tests, ou en HTTP pour un microservice futur).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Règle d'or à marteler trois fois&lt;/strong&gt; : dans MboloPay, &lt;code&gt;domain/port/in/&lt;/code&gt; et &lt;code&gt;domain/port/out/&lt;/code&gt; ne contiennent &lt;strong&gt;QUE&lt;/strong&gt; des interfaces Java. Aucune classe concrète. Aucune dépendance Spring. C'est vérifié par un test ArchUnit qui casse le build si tu glisses une classe. On y revient au §5.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Les 4 couches MboloPay
&lt;/h3&gt;

&lt;p&gt;Chaque module suit la même structure (ouvre &lt;code&gt;src/main/java/ga/banga/mbolopay/identite/&lt;/code&gt; sur le &lt;a href="https://github.com/bangaromaric/mbolopay" rel="noopener noreferrer"&gt;repo&lt;/a&gt; pour vérifier) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;identite/
├── domain/                 # ZÉRO framework. Le cœur.
│   ├── model/              # Abonne, AbonneId, NomGabonais, NumeroTelephoneGabonais
│   ├── event/              # EvenementAbonneCree (record)
│   ├── exception/          # NumeroDejaUtiliseException, NumeroNonAutoriseException
│   ├── port/in/            # CreerAbonneUseCase (interface)
│   ├── port/out/           # DepotAbonne, PublieurEvenements (interfaces)
│   └── service/            # logique métier pure
├── application/
│   └── service/            # CreerAbonneService — orchestration POJO
└── infrastructure/
    ├── primary/            # AbonneController (REST, ENTRE)
    └── secondary/          # DepotAbonneJpa, transactions, publishers (SORT)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et le mieux ? Dans la démo en ligne — &lt;a href="https://mbolopay.banga.ga/" rel="noopener noreferrer"&gt;https://mbolopay.banga.ga/&lt;/a&gt; — active le &lt;em&gt;mode pédagogique&lt;/em&gt; depuis &lt;code&gt;/profil&lt;/code&gt;, puis le &lt;em&gt;slow-mo&lt;/em&gt;. Crée un abonné. Tu verras l'opération traverser ces 4 couches &lt;strong&gt;en direct&lt;/strong&gt;, avec un curseur lumineux qui glisse de Primary → Application → Domain → Secondary. C'est rare qu'un projet pédagogique te laisse voir l'architecture &lt;em&gt;en mouvement&lt;/em&gt;. Profites-en.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 La règle absolue
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure dépend du Domaine. Jamais l'inverse.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si tu vois &lt;code&gt;import org.springframework.*&lt;/code&gt; dans une classe sous &lt;code&gt;domain/&lt;/code&gt;, c'est un bug. Si tu vois &lt;code&gt;@Entity&lt;/code&gt; sur un Aggregate Root, c'est un bug. Si tu vois &lt;code&gt;@Service&lt;/code&gt; sur un service d'application, c'est presque un bug — MboloPay rend ces services 100 % POJO et ajoute la transactionnalité par un &lt;em&gt;décorateur&lt;/em&gt; infrastructure. Le domaine reste pur, framework-free, testable sans Spring.&lt;/p&gt;

&lt;p&gt;Demande à l'IA &lt;em&gt;« sépare ce code en 4 couches hexagonales : primary, application, domain, secondary, avec ports in et out dans le domain »&lt;/em&gt;. L'IA le fait. Bien. Vite. Mais elle ne te dira &lt;strong&gt;jamais&lt;/strong&gt; &lt;em&gt;« tiens, il faudrait peut-être appliquer le pattern hexagonal ici »&lt;/em&gt; si tu ne le sais pas déjà — c'est ton job à toi.&lt;/p&gt;

&lt;p&gt;Tu trouves ça extrême ? Attends de voir comment Spring Modulith assemble plusieurs hexagones.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Spring Modulith — Assembler les bounded contexts proprement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Module = Bounded Context dans le monolithe
&lt;/h3&gt;

&lt;p&gt;Spring Modulith te permet d'avoir &lt;strong&gt;plusieurs bounded contexts dans le même monolithe Spring Boot&lt;/strong&gt;, avec des règles strictes vérifiées au runtime. Pas de microservices, pas de packaging Maven multi-module — juste des &lt;strong&gt;paquets Java&lt;/strong&gt; avec des frontières déclarées et un test qui s'assure que personne ne triche.&lt;/p&gt;

&lt;p&gt;C'est l'entre-deux idéal pour 90 % des projets : tu profites de la simplicité d'un monolithe (un seul JAR, une seule DB, des transactions ACID) tout en gardant la modularité d'un système distribué. Sans les emmerdes du réseau, du déploiement, et des transactions distribuées que personne ne sait vraiment debugger un samedi.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Trois mécanismes à connaître
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@ApplicationModule&lt;/code&gt;&lt;/strong&gt; sur le &lt;code&gt;package-info.java&lt;/code&gt; racine du module — déclare le type (&lt;code&gt;OPEN&lt;/code&gt;, &lt;code&gt;CLOSED&lt;/code&gt;, &lt;code&gt;NESTED&lt;/code&gt;) et les dépendances autorisées :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ApplicationModule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowedDependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"shared"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"identite :: events"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;ga.banga.mbolopay.portefeuille&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Traduction : le module &lt;code&gt;portefeuille&lt;/code&gt; peut dépendre de &lt;code&gt;shared&lt;/code&gt; (entièrement) et de la &lt;em&gt;named interface&lt;/em&gt; &lt;code&gt;events&lt;/code&gt; du module &lt;code&gt;identite&lt;/code&gt;. Pas du reste. S'il essaie d'importer &lt;code&gt;identite.domain.model.Abonne&lt;/code&gt;, le test échoue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@NamedInterface&lt;/code&gt;&lt;/strong&gt; sur un sous-paquet — l'expose à des modules sélectionnés. Le paquet &lt;code&gt;identite.domain.event&lt;/code&gt; est annoté &lt;code&gt;@NamedInterface("events")&lt;/code&gt; dans son &lt;code&gt;package-info.java&lt;/code&gt;. C'est la &lt;strong&gt;porte publique&lt;/strong&gt; d'&lt;code&gt;identite&lt;/code&gt; vers les autres modules. Le reste du module est invisible de l'extérieur.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@ApplicationModuleListener&lt;/code&gt;&lt;/strong&gt; — un listener cross-module qui s'exécute dans une transaction propre. Voici l'écouteur côté &lt;code&gt;portefeuille&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EcouteurEvenementAbonne&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CreerPortefeuilleUseCase&lt;/span&gt; &lt;span class="n"&gt;creerPortefeuille&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ApplicationModuleListener&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EvenementAbonneCree&lt;/span&gt; &lt;span class="n"&gt;evt&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;creerPortefeuille&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CommandeCreerPortefeuille&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;abonneId&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trois annotations, deux mondes parfaitement isolés. Et le code reste &lt;em&gt;lisible&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Le moment « OHHH »
&lt;/h3&gt;

&lt;p&gt;Tu réalises ? &lt;code&gt;portefeuille&lt;/code&gt; ne sait absolument rien de &lt;code&gt;Abonne&lt;/code&gt;. Il connaît juste un événement et un identifiant. Demain, on extrait &lt;code&gt;identite&lt;/code&gt; en microservice ? On change le listener pour écouter Kafka au lieu d'un event Spring local. Le reste est intact.&lt;/p&gt;

&lt;p&gt;C'est ça, la promesse tenue de &lt;strong&gt;DDD + Hexagonal + Modulith réunis&lt;/strong&gt;. Et c'est ça que MOUSSAVOU cherchait depuis 18 mois sans savoir le nommer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Un dernier mot sur l'IA :&lt;/em&gt; Claude, ChatGPT, Copilot connaissent parfaitement Spring Modulith. Les annotations, les listeners, les Named Interfaces — ils te génèrent tout ça parfaitement. Ce qu'aucun de ces outils ne peut faire pour toi : &lt;strong&gt;décider où passent les frontières entre tes bounded contexts&lt;/strong&gt;. Cette décision est purement métier. Elle vient de ta compréhension du problème, pas de l'IA. Et c'est précisément la décision la plus importante de ton architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. jMolecules et jSpecify — Les deux helpers qui changent tout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 jMolecules : rendre DDD lisible à la machine
&lt;/h3&gt;

&lt;p&gt;jMolecules est une mini bibliothèque qui fournit des annotations DDD : &lt;code&gt;@AggregateRoot&lt;/code&gt;, &lt;code&gt;@Identity&lt;/code&gt;, &lt;code&gt;@ValueObject&lt;/code&gt;, &lt;code&gt;@DomainEvent&lt;/code&gt;, etc. Aucune magie runtime — c'est juste du marquage déclaratif.&lt;/p&gt;

&lt;p&gt;Avantages concrets :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le code dit &lt;em&gt;exactement&lt;/em&gt; ce qu'il est. &lt;code&gt;@AggregateRoot class Abonne&lt;/code&gt; se lit en une seconde.&lt;/li&gt;
&lt;li&gt;Tu peux écrire des tests d'architecture qui se basent dessus.&lt;/li&gt;
&lt;li&gt;Des plugins (IntelliJ, autres outils) reconnaissent la sémantique et te guident.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.2 jSpecify : null-safety modernisée
&lt;/h3&gt;

&lt;p&gt;jSpecify est l'évolution de &lt;code&gt;@Nullable&lt;/code&gt;/&lt;code&gt;@NonNull&lt;/code&gt; en standard moderne supporté par Eclipse, IntelliJ, et bientôt javac. MboloPay annote ses paquets avec &lt;code&gt;@NullMarked&lt;/code&gt;, ce qui veut dire : &lt;em&gt;tout est non-null par défaut, sauf si explicitement marqué &lt;code&gt;@Nullable&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Conséquence pour MOUSSAVOU : moins de NPE en prod, plus de retours &lt;code&gt;Optional&amp;lt;T&amp;gt;&lt;/code&gt; invasifs partout, plus de bug silencieux « le numéro est null ». C'est gratuit, c'est moderne, c'est non-invasif. Tu peux l'adopter dans ton prochain projet ce soir.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Et les tests, dans tout ça ?
&lt;/h2&gt;

&lt;p&gt;Tu as compris la théorie. Mais qu'est-ce qui empêche un junior pressé de tout casser le vendredi à 17h ?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trois suites de tests&lt;/strong&gt;, qui cassent le build à la moindre violation :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;HexagonalArchitectureTest&lt;/code&gt;&lt;/strong&gt; (ArchUnit, 18 règles). Vérifie qu'aucune classe du domaine n'importe Spring, JPA, Jackson ou Jakarta Validation. Vérifie que &lt;code&gt;domain/port/in|out&lt;/code&gt; ne contiennent QUE des interfaces. Vérifie que toutes les exceptions métier héritent de &lt;code&gt;ExceptionDomaine&lt;/code&gt;. Vérifie que les &lt;code&gt;@RestController&lt;/code&gt; vivent UNIQUEMENT dans &lt;code&gt;infrastructure/primary/web/&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ModularityTests&lt;/code&gt;&lt;/strong&gt; (Spring Modulith). Vérifie la structure modulaire, les &lt;code&gt;allowedDependencies&lt;/code&gt; déclarées dans les &lt;code&gt;package-info.java&lt;/code&gt;, l'absence de cycles entre modules. Génère même un diagramme PlantUML automatiquement dans &lt;code&gt;target/spring-modulith-docs/&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tests unitaires métier classiques sur les VOs et Aggregates.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le truc essentiel ? Ces règles sont vérifiées par des tests qui &lt;strong&gt;CASSENT le build&lt;/strong&gt;. Si demain MOUSSAVOU importe &lt;code&gt;org.springframework.*&lt;/code&gt; dans &lt;code&gt;domain/&lt;/code&gt;, sa PR ne passe pas la CI. &lt;em&gt;L'architecture est devenue exécutable.&lt;/em&gt; C'est la différence entre « on a une convention » et « on a une garantie ». Ton lead dort mieux la nuit. Toi aussi.&lt;/p&gt;

&lt;p&gt;Pour voir, clone le repo et lance :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-Dtest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;HexagonalArchitectureTest,ModularityTests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;15 secondes pour valider que ton architecture tient debout. Imbattable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;En 2026 ces tests deviennent ton meilleur garde-fou contre l'IA.&lt;/strong&gt; Quand ChatGPT te génère un code qui marche mais qui glisse un &lt;code&gt;import org.springframework.*&lt;/code&gt; dans le domaine pour gagner du temps, ton CI s'écroule. Tu vois le bug. Tu redresses. Sans &lt;code&gt;HexagonalArchitectureTest&lt;/code&gt;, tu n'aurais rien vu — et ton code aurait dérivé en six mois.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. L'IA dans tout ça — 3 règles d'or pour les juniors en 2026
&lt;/h2&gt;

&lt;p&gt;Si tu es arrivé·e jusqu'ici, tu as compris le fond. Reste un sujet qu'on ne peut plus esquiver en 2026 : &lt;strong&gt;comment utiliser l'IA SANS qu'elle détruise ton architecture&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tu utilises probablement Claude, ChatGPT ou Copilot tous les jours. Moi aussi. Sans eux, MboloPay aurait pris deux ans au lieu de huit mois. &lt;strong&gt;Mais pendant ces huit mois, j'ai appris une chose que personne ne dit assez fort&lt;/strong&gt; : l'IA n'a pas d'opinion sur l'architecture. Elle exécute ce que tu lui demandes. Si tu lui demandes mal, elle exécute mal — vite.&lt;/p&gt;

&lt;p&gt;Trois règles d'or, durement apprises :&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 Apprends l'architecture AVANT de l'utiliser pour produire de l'architecture
&lt;/h3&gt;

&lt;p&gt;L'IA est l'assistant le plus brillant que tu auras jamais. Mais c'est un &lt;strong&gt;assistant&lt;/strong&gt;. Il accélère. Il n'oriente pas. Si tu lui demandes &lt;em&gt;« fais-moi une appli de mobile money »&lt;/em&gt;, il te génère un &lt;code&gt;MobileMoneyService&lt;/code&gt; de 600 lignes qui marche. C'est toi qui dois lui dire &lt;em&gt;« isole un bounded context identite et un bounded context portefeuille, communique par événement de domaine »&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Sans cette phrase, tu obtiens un monstre. Avec, tu obtiens MboloPay.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 Donne le vocabulaire métier exact
&lt;/h3&gt;

&lt;p&gt;Ce que Claude/ChatGPT/Copilot ne savent pas, c'est ton domaine. Mais ils savent EXACTEMENT comment écrire un Aggregate Root, un Value Object, une factory, un Domain Event, un &lt;code&gt;@ApplicationModuleListener&lt;/code&gt;. Le vocabulaire technique, ils l'ont avalé pendant leur entraînement.&lt;/p&gt;

&lt;p&gt;Ton job : leur donner ce vocabulaire enrichi du tien. Au lieu de &lt;em&gt;« valide le numéro de téléphone »&lt;/em&gt;, écris &lt;em&gt;« crée un Value Object &lt;code&gt;NumeroTelephoneGabonais&lt;/code&gt; avec validation E.164, opérateur Airtel/Moov détecté depuis le préfixe, et exception métier &lt;code&gt;NumeroNonAutoriseException&lt;/code&gt; qui hérite de &lt;code&gt;ExceptionDomaine&lt;/code&gt; »&lt;/em&gt;. L'IA livre. Parfait. En 30 secondes.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.3 Verrouille avec des tests d'architecture
&lt;/h3&gt;

&lt;p&gt;L'IA peut &lt;strong&gt;changer d'avis&lt;/strong&gt; au prochain prompt. Tu lui as dit hier de respecter les couches hexagonales ; demain elle glissera un &lt;code&gt;@Service&lt;/code&gt; dans le domaine parce que ce sera plus court. Tu ne verras pas tout.&lt;/p&gt;

&lt;p&gt;Tes 18 règles ArchUnit et tes &lt;code&gt;ModularityTests&lt;/code&gt; sont des &lt;strong&gt;garde-fous non-négociables&lt;/strong&gt;. Ils s'en fichent que le code soit de toi ou de Claude — si la règle est violée, le build casse. C'est ce qui empêche ton archi de pourrir silencieusement à chaque sprint.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;La phrase à retenir&lt;/strong&gt; : en 2026, le vrai super-pouvoir n'est plus de savoir CODER. C'est de savoir &lt;strong&gt;QUOI demander à l'IA&lt;/strong&gt;, et de &lt;strong&gt;comprendre ce qu'elle te rend&lt;/strong&gt;. Tout le reste de cet article t'a donné ce vocabulaire.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  8. À toi de jouer
&lt;/h2&gt;

&lt;p&gt;Quatre mini-exercices, du plus simple au plus formateur. Compte 30 minutes pour les quatre :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ouvre la démo&lt;/strong&gt; à &lt;a href="https://mbolopay.banga.ga/" rel="noopener noreferrer"&gt;https://mbolopay.banga.ga/&lt;/a&gt;. Active &lt;em&gt;Mode pédagogique&lt;/em&gt; dans &lt;code&gt;/profil&lt;/code&gt;. Crée un abonné avec un numéro Airtel (&lt;code&gt;+24107XXXXXXX&lt;/code&gt;). Repère les badges Q/C qui apparaissent et le flash de l'événement &lt;code&gt;EvenementAbonneCree&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Active le slow-mo&lt;/strong&gt; depuis le même &lt;code&gt;/profil&lt;/code&gt;. Fais un dépôt de 5000 FCFA. Observe l'overlay qui traverse les 4 couches Hexa. Identifie quel composant est dans la couche Application, lequel est dans Domain, lequel est dans Secondary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Va sur GitHub&lt;/strong&gt; : &lt;a href="https://github.com/bangaromaric/mbolopay" rel="noopener noreferrer"&gt;https://github.com/bangaromaric/mbolopay&lt;/a&gt;. Ouvre &lt;a href="https://github.com/bangaromaric/mbolopay/tree/main/src/main/java/ga/banga/mbolopay/portefeuille/domain/model/vo/Argent.java" rel="noopener noreferrer"&gt;&lt;code&gt;Argent.java&lt;/code&gt;&lt;/a&gt;. Liste les invariants appliqués au constructeur. Bonus : que se passe-t-il si tu passes un montant négatif ? Et un montant &lt;code&gt;null&lt;/code&gt; ?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lance les tests d'architecture localement&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git clone https://github.com/bangaromaric/mbolopay.git
   &lt;span class="nb"&gt;cd &lt;/span&gt;mbolopay
   ./mvnw &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-Dtest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;HexagonalArchitectureTest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puis va dans n'importe quelle classe &lt;code&gt;domain/&lt;/code&gt; et ajoute un &lt;code&gt;import org.springframework.stereotype.Service;&lt;/code&gt;. Relance le test. Regarde le build mourir avec un message d'erreur précis. Ressuscite-le en supprimant l'import. &lt;em&gt;Ressens le pouvoir.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;L'épreuve de l'IA&lt;/strong&gt;. Ouvre Claude ou ChatGPT. Tape : &lt;em&gt;« Crée une classe Java pour modéliser un numéro de téléphone gabonais. »&lt;/em&gt; Note ce que tu reçois. Puis tape : &lt;em&gt;« Refais-le comme un Value Object jMolecules &lt;code&gt;@ValueObject&lt;/code&gt; avec validation E.164 (&lt;code&gt;+241[067]\d{7}&lt;/code&gt;), opérateur Airtel/Moov détecté depuis le préfixe, et exception métier &lt;code&gt;NumeroNonAutoriseException&lt;/code&gt; qui hérite de &lt;code&gt;ExceptionDomaine&lt;/code&gt;. »&lt;/em&gt; Compare. Toute la différence est dans &lt;strong&gt;ton vocabulaire&lt;/strong&gt;. C'est ce que tu viens d'apprendre.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  9. Conclusion + Ressources
&lt;/h2&gt;

&lt;p&gt;Récap en cinq lignes — à retenir avant le prochain prompt que tu enverras à Claude :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DDD&lt;/strong&gt; = on modélise le métier, pas la technique. Bounded Contexts, Aggregates, Value Objects, Domain Events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hexagonal&lt;/strong&gt; = le domaine est au centre. Tout autour, des adaptateurs interchangeables. Le domaine reste framework-free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spring Modulith&lt;/strong&gt; = plusieurs Bounded Contexts dans un monolithe, avec frontières vérifiées au runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jMolecules&lt;/strong&gt; = annotations qui rendent DDD lisible à la machine (et à tes collègues).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jSpecify&lt;/strong&gt; = null-safety moderne, non-invasive, gratuite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ce que tu fais maintenant ? Tu clones MboloPay, tu lis le code, tu lances les tests, tu casses une règle volontairement pour voir, tu la répares. Ensuite, tu prends ton prochain projet perso (un mini blog, un gestionnaire de tâches, ton agrégateur de bourses universitaires) et tu essaies &lt;strong&gt;UN seul&lt;/strong&gt; de ces patterns dessus. Les Value Objects. C'est le plus rapide à essayer, c'est ce qui te marquera le plus vite.&lt;/p&gt;

&lt;p&gt;Pas tout d'un coup. Ne sois pas MOUSSAVOU qui passe deux nuits blanches à refactor tout son legacy. Sois MOUSSAVOU lundi matin : elle a appliqué un VO sur le numéro de téléphone, son lead a souri sur la PR, et elle a dormi le soir.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Une nuance d'honnêteté pour finir&lt;/strong&gt; : DDD/Hexagonal/Modulith ne sont &lt;strong&gt;pas&lt;/strong&gt; la bonne réponse pour tous les projets. Pour un CRUD pur avec 3 entités et 0 règle métier, c'est de l'over-engineering. Ces patterns brillent quand le métier est riche, quand l'équipe grandit, quand le projet doit vivre 5+ ans. La fintech de MOUSSAVOU coche les trois cases. Le tien probablement aussi, sinon tu ne lirais pas cet article.&lt;/p&gt;

&lt;p&gt;En 2026, savoir &lt;strong&gt;coder&lt;/strong&gt; ne suffit plus — l'IA le fait. Savoir &lt;strong&gt;demander à l'IA et comprendre sa réponse&lt;/strong&gt;, c'est ce qui te distingue. Toutes les notions de cet article (Bounded Context, Aggregate, Value Object, Port, Adapter, Named Interface) sont précisément le vocabulaire qui permet ce dialogue de qualité. Garde-les. Pratique-les. Sans elles, l'IA va plus vite — mais elle te conduira au précipice plus vite aussi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ressources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Démo MboloPay&lt;/strong&gt; : &lt;a href="https://mbolopay.banga.ga/" rel="noopener noreferrer"&gt;https://mbolopay.banga.ga/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo GitHub&lt;/strong&gt; : &lt;a href="https://github.com/bangaromaric/mbolopay" rel="noopener noreferrer"&gt;https://github.com/bangaromaric/mbolopay&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Livres&lt;/strong&gt; : &lt;em&gt;Domain-Driven Design&lt;/em&gt; (Eric Evans), &lt;em&gt;Implementing Domain-Driven Design&lt;/em&gt; (Vaughn Vernon), &lt;em&gt;Get Your Hands Dirty on Clean Architecture&lt;/em&gt; (Tom Hombergs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs en ligne&lt;/strong&gt; : &lt;a href="https://jmolecules.org" rel="noopener noreferrer"&gt;jmolecules.org&lt;/a&gt;, &lt;a href="https://jspecify.dev" rel="noopener noreferrer"&gt;jspecify.dev&lt;/a&gt;, &lt;a href="https://docs.spring.io/spring-modulith" rel="noopener noreferrer"&gt;docs.spring.io/spring-modulith&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bonne route. Et &lt;strong&gt;mbolo&lt;/strong&gt; si tu publies ton projet — partage le lien, j'irai lire.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Article rédigé en pensant à tous les MOUSSAVOU qui codent tard le soir dans les fintechs naissantes du continent, avec ou sans l'aide de Claude, ChatGPT ou Copilot. MboloPay est volontairement éducatif et open source — utilise-le, fork-le, casse-le, améliore-le, et publie ton propre article si l'envie te prend.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Auteur du projet MboloPay : BANGA Romaric.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>gcp</category>
      <category>architecture</category>
      <category>java</category>
    </item>
  </channel>
</rss>
