<?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>L'IA ne remplace pas les développeurs — elle dissout ce qui les unissait</title>
      <dc:creator>BANGA</dc:creator>
      <pubDate>Tue, 09 Jun 2026 18:11:44 +0000</pubDate>
      <link>https://dev.to/bangaromaric/lia-ne-remplace-pas-les-developpeurs-elle-dissout-ce-qui-les-unissait-3iae</link>
      <guid>https://dev.to/bangaromaric/lia-ne-remplace-pas-les-developpeurs-elle-dissout-ce-qui-les-unissait-3iae</guid>
      <description>&lt;p&gt;Lundi matin, Libreville. Ndong ouvre le dépôt avant même son café. Le sprint&lt;br&gt;
  prévoyait qu'il code trois endpoints cette semaine — le backend, son métier&lt;br&gt;
  depuis sept ans. Ils sont déjà là. Commités pendant le week-end. Signés&lt;br&gt;
  Mavoungou, le frontend de l'équipe. Avec l'IA.&lt;/p&gt;

&lt;p&gt;Le code compile. Les tests passent. Ndong n'est pas en colère. Mais une question&lt;br&gt;
  sourde s'installe : &lt;em&gt;suis-je encore nécessaire sur ce projet ?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;J'ai vu cette scène se rejouer dans plusieurs équipes, ici et ailleurs dans&lt;br&gt;
  l'écosystème francophone. On la résume toujours pareil : « l'IA va remplacer les&lt;br&gt;
  développeurs. » C'est faux. Et ce qui se passe vraiment est bien plus intéressant.&lt;/p&gt;

&lt;h2&gt;
  
  
  L'IA n'a pas supprimé des postes — elle a supprimé l'interdépendance
&lt;/h2&gt;

&lt;p&gt;Avant, collaborer n'était pas qu'une vertu. C'était une nécessité de structure.&lt;br&gt;
  Ndong avait besoin de Mavoungou, et l'inverse était vrai. Personne ne pouvait&lt;br&gt;
  couvrir le terrain de l'autre. Cette dépendance fabriquait de la coopération&lt;br&gt;
  presque mécaniquement.&lt;/p&gt;

&lt;p&gt;L'IA dissout cette nécessité. Et en la dissolvant, elle met au jour quelque chose&lt;br&gt;
  d'inconfortable :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Une bonne partie de ce qu'on appelait « esprit d'équipe » n'était parfois&lt;br&gt;
qu'une dépendance technique déguisée en fraternité.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Les équipes qui resteront soudées après l'IA, c'est qu'elles l'étaient déjà pour&lt;br&gt;
  de vrai.&lt;/p&gt;

&lt;h2&gt;
  
  
  La division silencieuse
&lt;/h2&gt;

&lt;p&gt;Le danger n'est pas la machine qui remplace l'humain. C'est plus sournois :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;L'IA ne te remplace pas directement. Elle donne à un collègue — ou à un chef&lt;br&gt;
qui code — de quoi justifier que tu n'es plus indispensable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;« Pourquoi garder cette personne si je produis 80 % de son travail avec l'IA ? »&lt;br&gt;
  La question a l'air rationnelle. Elle ne l'est qu'en surface. Mais elle suffit à&lt;br&gt;
  faire glisser une culture du « comment réussit-on ensemble ? » vers un « comment&lt;br&gt;
  je prouve que je réussis seul ? ».&lt;/p&gt;

&lt;h2&gt;
  
  
  Le piège des 20 %
&lt;/h2&gt;

&lt;p&gt;Les 80 % que l'IA te sort ont l'air complets. Ça compile, ça ressemble au travail&lt;br&gt;
  d'un expert. Mais les 20 % qui manquent, c'est exactement là que vit l'expertise :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les cas limites que personne n'avait vus venir ;&lt;/li&gt;
&lt;li&gt;les décisions d'architecture qu'on paie trois ans plus tard ;&lt;/li&gt;
&lt;li&gt;le métier : pourquoi le client Airtel et le client Moov n'ont pas la même
logique de réconciliation ;&lt;/li&gt;
&lt;li&gt;et surtout : savoir &lt;strong&gt;quand l'IA se trompe&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Celui qui met son collègue de côté ne voit pas ces 20 % — justement parce que les&lt;br&gt;
  80 % visibles sont convaincants. À court terme, le remplacement a l'air rationnel.&lt;br&gt;
  À long terme, il se paie en dette technique. Le genre qu'on rembourse en prod, un&lt;br&gt;
  samedi soir, quand le système lâche et que c'est toi qu'on appelle.&lt;/p&gt;

&lt;h2&gt;
  
  
  La vraie question
&lt;/h2&gt;

&lt;p&gt;« Est-ce que l'IA va remplacer les développeurs ? » La question est usée, et un&lt;br&gt;
  peu paresseuse. La vraie, celle qui décidera de l'avenir de nos équipes — à&lt;br&gt;
  Libreville comme à Douala ou à Dakar :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L'IA va-t-elle détruire la base réelle de notre collaboration, ou nous forcer à&lt;br&gt;
  la reconstruire sur autre chose que la simple dépendance technique ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La réponse ne dépend pas de la machine. Elle dépend de nous.&lt;/p&gt;




&lt;p&gt;J'ai développé l'anatomie complète de cette « division silencieuse » — la nuance&lt;br&gt;
  honnête (l'IA n'a rien inventé, elle a juste baissé le coût d'entrée de la&lt;br&gt;
  tentation), et comment reconstruire une collaboration &lt;em&gt;choisie&lt;/em&gt; plutôt que&lt;br&gt;
  &lt;em&gt;subie&lt;/em&gt; — dans l'essai original :&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://ban.ga/posts/ia-division-developpeurs/" rel="noopener noreferrer"&gt;Lire l'essai complet sur ban.ga →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Et dans votre équipe : l'IA vous rapproche-t-elle, ou commence-t-elle déjà, en&lt;br&gt;
  silence, à vous diviser ?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ia</category>
      <category>carriere</category>
      <category>teamwork</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Spring Native + Cloud Run : cold-start 18, RAM 3, facture à 0 FCFA</title>
      <dc:creator>BANGA</dc:creator>
      <pubDate>Tue, 26 May 2026 08:25:31 +0000</pubDate>
      <link>https://dev.to/bangaromaric/spring-native-cloud-run-cold-start-18-ram-3-facture-a-0-fcfa-fc7</link>
      <guid>https://dev.to/bangaromaric/spring-native-cloud-run-cold-start-18-ram-3-facture-a-0-fcfa-fc7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cet article est la version condensée de &lt;a href="https://ban.ga/posts/spring-native-cloud-run/" rel="noopener noreferrer"&gt;mon retour d'expérience complet sur ban.ga&lt;/a&gt; — config Maven &lt;code&gt;-Pnative&lt;/code&gt; détaillée, registrar &lt;code&gt;RuntimeHintsRegistrar&lt;/code&gt; pour la console H2, pipeline PowerShell de 200 lignes, et chaque piège décortiqué.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  23h47, un dimanche soir à Libreville
&lt;/h2&gt;

&lt;p&gt;Screenshot WhatsApp d'une dev fictive : sa fintech crash silencieux sur l'OTP en prod.&lt;/p&gt;

&lt;p&gt;Cold-start Spring Boot sur Cloud Run : &lt;strong&gt;3 740 ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Lundi matin, première vraie facture GCP. Quelques milliers de FCFA — pour une dev qui paye son cloud avec sa bourse de fin d'études, c'est un choc.&lt;/p&gt;

&lt;p&gt;MOUSSAVOU, l'héroïne fictive que je trimballe d'article en article, vient de découvrir le truc à la dure. Tu peux ranger ton code en DDD + Hexagonal + Spring Modulith pendant six mois. Si ta JVM met près de 4 secondes à booter, les utilisateurs reçoivent leur OTP avant que &lt;code&gt;/verifier&lt;/code&gt; ne soit prêt à le valider. Re-saisie. Re-cold-start. Re-perte.&lt;/p&gt;

&lt;h2&gt;
  
  
  Le calcul économique qui pique
&lt;/h2&gt;

&lt;p&gt;C'est là que ça devient désagréable. Un cold-start de 3,8 secondes, c'est &lt;strong&gt;3,8 secondes de vCPU et de RAM facturées par Cloud Run&lt;/strong&gt;, qu'il y ait une transaction qui aboutisse au bout ou pas. Chaque utilisateur qui timeout retry. Chaque retry réveille une nouvelle instance, donc un nouveau cold-start.&lt;/p&gt;

&lt;p&gt;Sortons la calculette. 200 utilisateurs le matin × 3 retries × 3,8 s :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;= 2 280 vCPU-secondes brûlées
  avant qu'une seule transaction n'aboutisse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus 182 GB-secondes de RAM, pour la même raison. Le &lt;code&gt;min-instances=0&lt;/code&gt; protège la facture la nuit. Il ne fait &lt;em&gt;rien&lt;/em&gt; contre les cascades d'échec le matin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Le bench
&lt;/h2&gt;

&lt;p&gt;J'ai recompilé MboloPay (mini mobile money open source) en natif avec GraalVM. &lt;strong&gt;Même code Java 25. Compilation différente.&lt;/strong&gt; Chiffres bruts mesurés le 16 mai 2026 sur Spring Boot 4.0.2, Liberica NIK 25, Hibernate 7.2.1, H2 in-memory :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Étape&lt;/th&gt;
&lt;th&gt;JVM Java 25&lt;/th&gt;
&lt;th&gt;Natif GraalVM&lt;/th&gt;
&lt;th&gt;Gain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Boot → Tomcat init&lt;/td&gt;
&lt;td&gt;1 207 ms&lt;/td&gt;
&lt;td&gt;36 ms&lt;/td&gt;
&lt;td&gt;×33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hikari pool startup&lt;/td&gt;
&lt;td&gt;559 ms&lt;/td&gt;
&lt;td&gt;65 ms&lt;/td&gt;
&lt;td&gt;×8.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JPA init&lt;/td&gt;
&lt;td&gt;774 ms&lt;/td&gt;
&lt;td&gt;14 ms&lt;/td&gt;
&lt;td&gt;×55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JPA → Tomcat 8080&lt;/td&gt;
&lt;td&gt;875 ms&lt;/td&gt;
&lt;td&gt;76 ms&lt;/td&gt;
&lt;td&gt;×11.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total « Started in »&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3 740 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;205 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;×18,2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM résident&lt;/td&gt;
&lt;td&gt;~250 Mo&lt;/td&gt;
&lt;td&gt;~80 Mo&lt;/td&gt;
&lt;td&gt;÷3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build CI&lt;/td&gt;
&lt;td&gt;~10 s&lt;/td&gt;
&lt;td&gt;7-12 min&lt;/td&gt;
&lt;td&gt;×12 (compromis)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La ligne qui fait mal : &lt;strong&gt;JPA init passe de 774 ms à 14 ms (×55)&lt;/strong&gt;. Hibernate fait énormément de réflexion pour mapper entités → tables. Le AOT processing pré-calcule ce mapping à la compilation. Le runtime n'a plus qu'à brancher.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comment ça marche, sans la magie
&lt;/h2&gt;

&lt;p&gt;Spring AOT fait le travail à la compilation. Trois mécanismes empilés :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Closed-world analysis&lt;/strong&gt; — GraalVM considère que ton application est fermée. Aucune classe ne sera ajoutée au runtime. À partir du &lt;code&gt;main()&lt;/code&gt;, il suit toutes les références, marque les classes utilisées, jette le reste.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Génération de code direct&lt;/strong&gt; — Spring Boot 4 inclut un plugin Maven, &lt;code&gt;spring-boot-maven-plugin:process-aot&lt;/code&gt;, qui s'exécute &lt;strong&gt;avant&lt;/strong&gt; la compilation native. Il lit ta config, simule le démarrage, génère du code Java qui &lt;em&gt;remplace&lt;/em&gt; la réflexion runtime par des appels directs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native image&lt;/strong&gt; — GraalVM compile ce code direct en exécutable natif (ELF sous Linux, &lt;code&gt;.exe&lt;/code&gt; sous Windows). Plus de JVM au runtime. Plus de JIT. Plus de classpath scan.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;Note de terminologie&lt;/strong&gt;. &lt;em&gt;« Spring Native »&lt;/em&gt; était à l'origine un projet incubator séparé (le module &lt;code&gt;spring-native&lt;/code&gt;, 2021-2022). Depuis &lt;strong&gt;Spring Boot 3 (novembre 2022)&lt;/strong&gt;, ce support a été &lt;strong&gt;intégré directement au core Spring Boot&lt;/strong&gt;. Le nom &lt;em&gt;« Spring Native »&lt;/em&gt; reste largement utilisé par habitude.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Le point clé : &lt;strong&gt;ton code Spring ne change pas&lt;/strong&gt;. &lt;code&gt;@RestController&lt;/code&gt;, &lt;code&gt;@Service&lt;/code&gt;, &lt;code&gt;@Repository&lt;/code&gt;, &lt;code&gt;@Transactional&lt;/code&gt;, tout marche. Ce qui change, c'est le &lt;em&gt;quand&lt;/em&gt; : ce que Spring fait normalement au démarrage de la JVM, il le fait maintenant au moment du &lt;code&gt;mvnw -Pnative&lt;/code&gt;. Le runtime n'a plus rien à apprendre, il sait déjà.&lt;/p&gt;

&lt;h2&gt;
  
  
  Activer le profil natif tient en 6 lignes
&lt;/h2&gt;

&lt;p&gt;C'est la partie qui surprend toujours quand je la montre :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.graalvm.buildtools&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;native-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pas de version explicite (gérée par &lt;code&gt;spring-boot-starter-parent&lt;/code&gt;). Pas de config custom. Tu lances :&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="nt"&gt;-Pnative&lt;/span&gt; native:compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou pour produire directement une image Docker :&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="nt"&gt;-Pnative&lt;/span&gt; spring-boot:build-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring Boot délègue à &lt;strong&gt;Paketo Buildpacks&lt;/strong&gt; (builder &lt;code&gt;paketobuildpacks/builder-noble-java-tiny&lt;/code&gt;). Pas de Dockerfile. Le buildpack détecte le mode natif, télécharge GraalVM, compile, et produit une image OCI minuscule prête à pousser sur Artifact Registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Les 5 pièges qui m'ont coûté 2 jours
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Piège 1 — La console H2 ne marche plus
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;http://localhost:8080/h2-console&lt;/code&gt; te renvoie une page blanche en natif. La servlet H2 utilise de la réflexion dynamique non hint-ée par Spring AOT.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution honnête&lt;/strong&gt; : dev en JVM standard (&lt;code&gt;./mvnw spring-boot:run&lt;/code&gt;), prod en natif. Si tu veux &lt;em&gt;vraiment&lt;/em&gt; la console en natif, il faut écrire un &lt;code&gt;RuntimeHintsRegistrar&lt;/code&gt; custom (squelette complet sur &lt;a href="https://ban.ga/posts/spring-native-cloud-run/#pi%C3%A8ge-1" rel="noopener noreferrer"&gt;ban.ga&lt;/a&gt;) — quelques dizaines de lignes, à construire &lt;strong&gt;incrémentalement en réagissant aux &lt;code&gt;ClassNotFoundException&lt;/code&gt;&lt;/strong&gt; qui pop dans les logs Cloud Run. Plan une demi-journée.&lt;/p&gt;

&lt;h3&gt;
  
  
  Piège 2 — Spring Boot DevTools silencieusement ignoré
&lt;/h3&gt;

&lt;p&gt;Tu as ajouté &lt;code&gt;spring-boot-devtools&lt;/code&gt; au &lt;code&gt;pom.xml&lt;/code&gt;. En JVM, hot-reload. En natif, &lt;strong&gt;ignoré sans erreur&lt;/strong&gt;. Pas de log. Juste pas de hot-reload.&lt;/p&gt;

&lt;p&gt;C'est logique : DevTools repose sur un classloader custom qui recharge les classes à chaud. Un binaire natif n'a pas de classloader. Tu ne peux pas hot-reloader du code machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Piège 3 — Les erreurs AOT bloquent le build
&lt;/h3&gt;

&lt;p&gt;Le AOT processing tourne &lt;strong&gt;avant&lt;/strong&gt; la compilation native. Si ton code a une erreur que l'AOT détecte (bean qui dépend d'une classe absente, circularité, config invalide), le build s'arrête. Aucun binaire ne sort.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:4.0.2:process-aot
[ERROR] Caused by: java.lang.IllegalStateException: Failed to read candidate component class
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C'est un faux ami : tu crois que c'est un problème natif, c'est en réalité un problème Spring détecté plus tôt qu'à la normale. &lt;strong&gt;Méthode debug&lt;/strong&gt; : assure-toi d'abord que &lt;code&gt;./mvnw spring-boot:run&lt;/code&gt; démarre sans warning. Si la JVM démarre proprement, l'AOT démarrera aussi neuf fois sur dix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Piège 4 — Liberica NIK ≠ Liberica JDK
&lt;/h3&gt;

&lt;p&gt;Le grand classique qui fait perdre une demi-journée. Tu as installé Liberica JDK 25. Tu lances &lt;code&gt;./mvnw -Pnative native:compile&lt;/code&gt;. Tu obtiens :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;native-image is not installed in your JAVA_HOME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tu as installé la &lt;strong&gt;JDK&lt;/strong&gt; Liberica 25, pas le &lt;strong&gt;NIK&lt;/strong&gt; (Native Image Kit). La JDK seule ne contient pas &lt;code&gt;native-image&lt;/code&gt;. Il te faut &lt;a href="https://bell-sw.com/pages/downloads/native-image-kit/" rel="noopener noreferrer"&gt;Liberica NIK 25&lt;/a&gt;, une distribution séparée qui inclut JDK + GraalVM + native-image.&lt;/p&gt;

&lt;p&gt;Sous Windows, prévois aussi &lt;strong&gt;Visual Studio Build Tools&lt;/strong&gt; (charge de travail &lt;em&gt;« Développement Desktop en C++ »&lt;/em&gt;) — GraalVM Native Image utilise &lt;code&gt;cl.exe&lt;/code&gt; (le compilateur MSVC). Lance la compilation depuis le &lt;em&gt;« x64 Native Tools Command Prompt for VS 2022 »&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Piège 5 — &lt;code&gt;docker-credential-gcr&lt;/code&gt; obligatoire sous Windows
&lt;/h3&gt;

&lt;p&gt;Sur macOS et Linux, &lt;code&gt;gcloud auth configure-docker&lt;/code&gt; installe correctement un helper. Sur Windows, il configure un helper qui pointe sur &lt;code&gt;docker-credential-gcloud.exe&lt;/code&gt;… qui n'existe pas dans le PATH. À la fin de 10 minutes de build :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cannot run program "docker-credential-gcloud": CreateProcess error=2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Il faut installer &lt;strong&gt;docker-credential-gcr&lt;/strong&gt; séparément, l'ajouter au PATH, et :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-credential-gcr configure-docker --registries=&amp;lt;region&amp;gt;-docker.pkg.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et &lt;strong&gt;ne pas oublier &lt;code&gt;gcloud auth application-default login&lt;/code&gt; (ADC) en plus de &lt;code&gt;gcloud auth login&lt;/code&gt;&lt;/strong&gt;. Ce sont deux mécanismes d'authentification distincts. Sans les deux, push échoue avec &lt;code&gt;auth: "invalid_grant"&lt;/code&gt; — &lt;strong&gt;après 10 minutes de build&lt;/strong&gt;. Rageant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quand NE PAS faire du natif
&lt;/h2&gt;

&lt;p&gt;Soyons honnêtes. Le natif n'est pas la solution à tout.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tu changes ton code 30 fois par jour&lt;/strong&gt; — early-stage produit, sprint design-prod-design. Le build natif de 7-12 minutes en CI va te rendre fou. Reste sur JVM standard, déploie en 2 minutes, garde tes itérations courtes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ton service tourne 24/7 sans jamais scale à zéro&lt;/strong&gt; — trafic continu, instance jamais froide. Le cold-start n'est jamais payé. Pire : après warm-up JIT, la JVM peut être &lt;strong&gt;10-20 % plus rapide en débit&lt;/strong&gt; que ce qu'AOT a prédit à la compilation. &lt;strong&gt;Le natif gagne sur les workloads serverless / intermittents, pas sur les workloads stables.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tu utilises massivement de la réflexion dynamique ou des libs tierces non Spring&lt;/strong&gt; — tu peux y arriver, mais le coût d'ingénierie devient significatif. Mesure avant.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Résultat MboloPay
&lt;/h2&gt;

&lt;p&gt;Facture revenue à &lt;strong&gt;0 FCFA / mois&lt;/strong&gt; tant que la démo reste sous les 2 millions de requêtes du free tier GCP. &lt;code&gt;min-instances=0&lt;/code&gt;. Cold-start mesuré : 205 ms. Sur un OTP fintech, c'est la différence entre une transaction qui passe et une transaction perdue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pour aller plus loin
&lt;/h2&gt;

&lt;p&gt;Cet article est la version condensée. Sur &lt;a href="https://ban.ga/posts/spring-native-cloud-run/" rel="noopener noreferrer"&gt;ban.ga/posts/spring-native-cloud-run/&lt;/a&gt;, tu trouveras :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le &lt;code&gt;RuntimeHintsRegistrar&lt;/code&gt; complet pour la console H2 (et pourquoi c'est &lt;em&gt;« faisable mais pénible »&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Le pipeline PowerShell de ~200 lignes qui orchestre git → bump → build natif → push → deploy → smoke-test&lt;/li&gt;
&lt;li&gt;La config &lt;code&gt;gcloud run deploy&lt;/code&gt; détaillée avec les bons paramètres &lt;code&gt;--concurrency&lt;/code&gt; / &lt;code&gt;--cpu&lt;/code&gt; / &lt;code&gt;--memory&lt;/code&gt; pour le natif&lt;/li&gt;
&lt;li&gt;Pourquoi &lt;code&gt;min-instances=1&lt;/code&gt; &lt;em&gt;« pour éviter les cold-starts »&lt;/em&gt; est généralement une erreur quand tu es déjà en natif&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le repo MboloPay est ouvert sur GitHub : &lt;a href="https://github.com/bangaromaric/mbolopay" rel="noopener noreferrer"&gt;github.com/bangaromaric/mbolopay&lt;/a&gt;. La démo tourne sur &lt;a href="https://mbolopay.banga.ga" rel="noopener noreferrer"&gt;mbolopay.banga.ga&lt;/a&gt; (Cloud Run, &lt;code&gt;min-instances=0&lt;/code&gt;, cold-start le matin, 200 ms le reste du temps).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Et vous ?&lt;/strong&gt; Vous l'avez tenté en prod ? Quel piège vous a fait perdre le plus de temps ?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Merci à &lt;a href="https://www.linkedin.com/in/yannick-serge-obam/" rel="noopener noreferrer"&gt;Yannick Serge Obam&lt;/a&gt; pour sa relecture exigeante qui a rendu l'article plus juste.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>spring</category>
      <category>graalvm</category>
      <category>gcp</category>
    </item>
    <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>
