Développement Web

Architecture logicielle moderne : DDD, microservices et event-driven

18 min de lecture

Une application qui démarre comme quelques centaines de lignes finit, deux ans plus tard, en un enchevêtrement où le moindre changement casse trois écrans imprévus. Ce n’est presque jamais un problème de langage ou de framework : c’est un problème d’architecture. La façon dont vous découpez votre code, isolez vos règles métier et faites communiquer vos composants détermine si votre logiciel reste modifiable… ou devient un boulet. Ce guide principal cartographie les quatre grandes approches qui structurent les applications sérieuses aujourd’hui — le Domain-Driven Design, l’architecture hexagonale, les microservices et l’architecture orientée événements — et vous oriente, étape par étape, vers les tutoriels pratiques qui les mettent en œuvre.

L’angle est volontairement concret. Plutôt que d’aligner des définitions, nous construisons un fil conducteur unique tout au long de cette série : la plateforme de gestion de commandes d’une boutique. Catalogue de produits, prise de commande, suivi du stock, livraison, facturation : ce domaine simple en apparence révèle, dès qu’on l’examine de près, tous les enjeux d’architecture qu’une grosse application doit résoudre. Chaque tutoriel en bâtit une brique, et ce guide explique comment les briques s’emboîtent.

Sommaire

🎯 Ce que ce parcours vous permettra de faire

À l’issue de cette série, vous ne vous contenterez pas de reconnaître des mots à la mode dans une offre d’emploi. Vous serez capable de poser des choix d’architecture argumentés et de les défendre devant une équipe :

  • Découper un domaine métier en contextes bornés cohérents, et nommer chaque concept avec un vocabulaire que les non-développeurs comprennent.
  • Structurer une application pour que ses règles métier ne dépendent ni de la base de données, ni du framework web, ni d’aucun outil externe.
  • Décider quand un découpage en microservices est justifié — et, plus important encore, quand il ne l’est pas.
  • Faire dialoguer des composants par messages asynchrones plutôt que par appels directs, et comprendre les compromis que cela impose.
  • Séparer le modèle de lecture du modèle d’écriture, et reconstruire l’état d’un système à partir de son flux d’événements.
  • Gérer une transaction qui traverse plusieurs services sans verrou distribué, à l’aide du pattern Saga.

🗺️ Le parcours d’apprentissage

L’ordre des tutoriels n’est pas arbitraire : il suit la maturité des idées, du modèle métier vers la distribution. Si vous débutez en architecture, suivez-le linéairement ; si vous cherchez une réponse précise, sautez directement à l’étape concernée.

  1. Modéliser le domaine (DDD) — on commence par comprendre ce que fait réellement la boutique avant d’écrire la moindre couche technique. Voir le tutoriel.
  2. Isoler le cœur (hexagonale) — on protège ce modèle des détails techniques grâce aux ports et adaptateurs. Voir le tutoriel.
  3. Découper progressivement (microservices) — on extrait un service du monolithe sans tout réécrire, via le pattern Strangler Fig. Voir le tutoriel.
  4. Découpler par les événements — on relie les services par une messagerie asynchrone pour qu’une panne de l’un n’arrête pas les autres. Voir le tutoriel.
  5. Séparer lecture et écriture (CQRS / Event Sourcing) — on optimise indépendamment les deux faces du système et on conserve l’historique complet. Voir le tutoriel.
  6. Coordonner les transactions distribuées (Saga) — on assure la cohérence d’une commande qui touche stock, paiement et livraison. Voir le tutoriel.

Pourquoi l’architecture décide du sort d’un projet

Une vérité dérange les équipes pressées : la plupart des coûts d’un logiciel n’apparaissent pas à l’écriture, mais à la modification. On code une fonctionnalité une fois, on la fait évoluer dix fois. Une architecture saine optimise donc le coût du changement, pas la rapidité du premier jet. C’est exactement ce que mesurent les recherches sur la performance des équipes : les organisations capables de livrer souvent et sans casse partagent des propriétés structurelles précises, indépendamment du langage employé.

Deux notions résument tout. Le couplage décrit à quel point un changement dans un module force un changement dans un autre. La cohésion décrit à quel point les éléments d’un même module servent un objectif commun. L’objectif universel de l’architecture tient en une phrase : viser un couplage faible entre modules et une cohésion forte à l’intérieur de chaque module. Tout le reste — DDD, hexagonal, microservices — n’est qu’une déclinaison de ce principe à des échelles différentes.

On confond souvent architecture et infrastructure. Pourtant, choisir entre un serveur unique et un cluster Kubernetes relève du déploiement ; décider que la logique de calcul d’une commande ne doit jamais dépendre du système de paiement relève de l’architecture. La première décision est réversible en quelques heures ; la seconde, une fois figée dans des milliers de lignes, peut coûter des mois à corriger. C’est pourquoi un développeur qui maîtrise ces patterns vaut, sur le marché, bien plus qu’un simple exécutant.

Les concepts fondamentaux

Avant de plonger dans chaque approche, fixons un socle de vocabulaire. Ces notions reviennent dans tous les tutoriels et il vaut mieux les avoir en tête une fois pour toutes.

La dépendance et son sens

Quand un module A appelle un module B, on dit que A dépend de B. La direction de cette flèche est cruciale. Une règle de bon sens, formalisée par Robert C. Martin sous le nom de principe d’inversion des dépendances, dit que le code de haut niveau (vos règles métier) ne doit pas dépendre du code de bas niveau (la base de données, le réseau). C’est l’inverse qui doit être vrai : les détails techniques dépendent du métier. Ce simple renversement de flèche est le moteur de l’architecture hexagonale.

Le langage partagé

Dans une boutique, un « panier » n’a pas le même sens pour le marketing, la comptabilité et la logistique. Si le code emploie un seul mot « panier » pour trois réalités, il deviendra incompréhensible. Le DDD répond à ce piège par le langage omniprésent : un vocabulaire négocié avec les experts métier, utilisé identiquement dans les conversations et dans le code.

La synchronie et l’asynchronie

Un appel synchrone attend une réponse : le client est bloqué tant que le serveur n’a pas répondu. Un message asynchrone est déposé dans une file et traité plus tard : l’émetteur continue sa route immédiatement. Tout le débat entre architecture orientée requêtes et architecture orientée événements tient dans ce choix, et il a des conséquences profondes sur la résilience et la cohérence des données.

Domain-Driven Design : aligner le code sur le métier

Formalisé par Eric Evans dans son ouvrage de référence paru en 2003, le Domain-Driven Design part d’un constat simple : les logiciels échouent rarement à cause de la technique, mais parce que le code ne reflète pas fidèlement le métier qu’il prétend servir. Le DDD propose donc de placer le modèle du domaine au centre, et de construire la technique autour.

Sa contribution la plus structurante est le contexte borné (bounded context). Au lieu de chercher un modèle unique qui décrirait toute l’entreprise — quête vouée à l’échec — on découpe le domaine en zones autonomes, chacune avec son propre modèle et son propre langage. Dans notre boutique, le contexte « Catalogue » manipule des produits avec des descriptions et des photos ; le contexte « Stock » manipule les mêmes produits, mais ne s’intéresse qu’aux quantités et aux emplacements. Ce sont deux modèles distincts du même mot, et c’est sain. Cette frontière conceptuelle deviendra, plus tard, la frontière naturelle entre deux microservices.

À l’intérieur d’un contexte, le DDD fournit des briques tactiques : les entités (objets identifiés par un identifiant stable, comme une commande), les objets-valeurs (objets définis par leurs attributs, comme une adresse ou un montant), les agrégats (grappes d’objets traitées comme une unité de cohérence) et les dépôts (l’abstraction qui range et retrouve les agrégats). Le tutoriel consacré au DDD construit ces briques en TypeScript autour de la commande de la boutique, et montre comment une règle comme « on ne peut pas commander plus que le stock disponible » trouve naturellement sa place dans l’agrégat.

Architecture hexagonale : isoler le cœur

Décrite par Alistair Cockburn dès le milieu des années 1990 puis rebaptisée ports et adaptateurs en 2005, l’architecture hexagonale répond à une question précise : comment empêcher que la logique métier ne soit contaminée par les détails techniques ? Sa réponse est élégante. On place le domaine au centre d’un hexagone. Tout ce qui l’entoure — la base de données, l’API web, la messagerie, les services tiers — communique avec lui à travers des ports, qui sont de simples interfaces, et des adaptateurs, qui sont les implémentations concrètes de ces interfaces.

Le bénéfice se mesure le jour où vous voulez changer de base de données, ou simplement tester votre code. Comme le domaine ne connaît que le port « je sais sauvegarder une commande » et jamais la classe PostgreSQL qui le réalise, vous pouvez brancher une implémentation en mémoire pour vos tests, et l’implémentation réelle en production, sans toucher une ligne du métier. Les règles deviennent testables en millisecondes, sans démarrer le moindre serveur.

Cette discipline n’est pas réservée aux microservices : elle s’applique aussi bien à un monolithe. D’ailleurs, un monolithe bien hexagonalisé est souvent la meilleure rampe de lancement vers une future décomposition, car ses frontières internes sont déjà nettes. Le tutoriel hexagonal structure le service de commandes de la boutique avec NestJS et montre, en pratique, comment l’inversion de dépendance rend le cœur indépendant du framework lui-même.

Microservices : découper pour mieux livrer

Popularisé vers 2014 par James Lewis et Martin Fowler, le style microservices consiste à bâtir une application comme un ensemble de petits services indépendants, chacun déployable séparément et possédant ses propres données. L’attrait est réel : des équipes autonomes, des déploiements ciblés, une montée en charge sélective. Mais le piège l’est tout autant. Un découpage prématuré transforme des appels de fonction triviaux en appels réseau faillibles, et remplace une base de données cohérente par un casse-tête de cohérence distribuée.

La règle d’or tient en une phrase souvent attribuée à la communauté : ne commencez pas par des microservices. Commencez par un monolithe bien structuré — idéalement modulaire et hexagonal — et n’extrayez un service que lorsqu’un besoin concret l’exige : une partie qui doit monter en charge indépendamment, une équipe qui a besoin de livrer à son propre rythme, un domaine dont le cycle de vie diffère du reste. Le critère de découpage le plus fiable reste le contexte borné identifié grâce au DDD.

La question du quand et du comment décider mérite un traitement à part entière ; elle est développée dans l’article Architecture microservices : quand et comment découper son application. Pour la mise en œuvre, plutôt que la réécriture risquée d’un système qui fonctionne, on privilégie une migration progressive. Le tutoriel Strangler Fig montre comment intercaler une façade devant le monolithe et déplacer une fonctionnalité à la fois vers un service Spring Boot, jusqu’à ce que l’ancien système s’éteigne de lui-même.

Architecture orientée événements

Dès qu’on a plusieurs services, se pose la question de leur communication. La voie la plus intuitive — chaque service appelle directement les autres en HTTP — recrée un couplage fort sous une forme déguisée : si le service de facturation est en panne, la prise de commande échoue elle aussi. L’architecture orientée événements renverse la logique. Au lieu de demander aux autres d’agir, un service annonce qu’un fait s’est produit : « la commande 4072 a été confirmée ». Les services intéressés réagissent à ce fait, chacun à son rythme, via un intermédiaire de messagerie.

Le gain est une autonomie réelle. Le service de commandes ignore qui écoute son événement ; on peut ajouter un service de notification ou de statistiques sans jamais le modifier. La contrepartie est la cohérence à terme (eventual consistency) : pendant un court instant, la commande existe mais la facture n’est pas encore émise. Accepter ce délai, le rendre visible aux utilisateurs et le borner, fait partie du métier d’architecte.

Le tutoriel orienté événements met cela en pratique avec RabbitMQ et FastAPI : un service publie l’événement de confirmation, deux autres s’y abonnent, et l’on observe le découplage à l’œuvre. Pour les flux à très fort volume, un journal d’événements comme Apache Kafka prend le relais ; le comparatif des solutions de streaming aide à choisir l’intermédiaire adapté à votre charge.

CQRS et Event Sourcing

Deux patterns souvent cités ensemble, mais distincts. CQRS (Command Query Responsibility Segregation), dont le terme a été popularisé par Greg Young en s’appuyant sur le principe de séparation commande-requête de Bertrand Meyer, consiste à utiliser deux modèles différents : un pour modifier les données (les commandes) et un pour les lire (les requêtes). Cette séparation permet d’optimiser chaque face indépendamment — par exemple, des écritures normalisées et des lectures pré-calculées pour un tableau de bord rapide.

L’Event Sourcing va plus loin : au lieu de stocker l’état courant d’une commande, on stocke la suite des événements qui l’ont produite (créée, article ajouté, payée, expédiée). L’état actuel se reconstruit en rejouant ces événements. On gagne un audit parfait, la capacité de remonter dans le temps et une source de vérité naturelle pour l’architecture événementielle. On y perd en simplicité : les requêtes nécessitent des projections, et certaines corrections deviennent délicates. Le tutoriel dédié construit un petit magasin d’événements en TypeScript et une projection de lecture, pour que ces idées cessent d’être abstraites.

Saga : les transactions à l’échelle distribuée

Une commande confirmée doit débiter le stock, encaisser le paiement et planifier la livraison. Dans un monolithe, une transaction de base de données garantit que tout réussit ou que tout est annulé. Dès que ces trois opérations vivent dans trois services aux bases distinctes, cette garantie disparaît : il n’existe pas de transaction unique qui les embrasse toutes sans introduire des verrous distribués coûteux et fragiles.

Le pattern Saga, dont l’idée fondatrice remonte à un article de Hector Garcia-Molina et Kenneth Salem publié en 1987, résout le problème autrement. Une saga est une suite de transactions locales : chaque service fait son travail et publie le résultat. Si une étape échoue, on ne fait pas marche arrière par un rollback classique — impossible entre bases séparées — mais par des transactions de compensation qui défont logiquement ce qui a été fait (rembourser le paiement, recréditer le stock). Deux styles existent : la chorégraphie, où chaque service réagit aux événements des autres, et l’orchestration, où un coordinateur central pilote la séquence. Le tutoriel Saga implémente les deux avec Spring Boot et en explique les compromis.

Les tutoriels de ce parcours

Chaque tutoriel ci-dessous construit une brique de la plateforme de commandes et peut se suivre seul, mais l’ensemble forme une progression cohérente :

En complément sur la communication inter-services, le tutoriel Spring Cloud Gateway traite du point d’entrée unique, tandis que Resilience4j couvre les disjoncteurs et les tentatives, indispensables dès qu’un service en appelle un autre.

Erreurs fréquentes à éviter

Erreur Cause Correctif
Découper en microservices dès le premier jour On confond modularité et distribution Démarrer par un monolithe modulaire, extraire un service seulement sur besoin avéré
Partager une base de données entre services Volonté d’éviter la duplication Chaque service possède ses données ; échanger par événements ou API, jamais par tables communes
Mettre la logique métier dans les contrôleurs Rapidité apparente au départ Isoler le domaine au centre (hexagonal) ; les contrôleurs ne font qu’adapter l’entrée et la sortie
Croire que l’asynchrone est gratuit On sous-estime la cohérence à terme Rendre les délais visibles, concevoir des consommateurs idempotents, prévoir la compensation
Adopter Event Sourcing partout Effet de mode Le réserver aux domaines où l’historique et l’audit ont une vraie valeur métier
Un seul modèle pour toute l’entreprise Recherche d’un schéma « universel » Accepter plusieurs modèles via des contextes bornés ; un même mot peut signifier deux choses

Questions fréquentes

Faut-il maîtriser le DDD avant de faire des microservices ?
Ce n’est pas obligatoire, mais c’est fortement recommandé. Le DDD fournit le critère de découpage le plus fiable : un microservice bien conçu correspond presque toujours à un contexte borné. Sans cette boussole, on découpe selon des couches techniques et on obtient des services qui ne sont jamais réellement indépendants.

Architecture hexagonale et microservices, est-ce la même chose ?
Non. L’hexagonale est un patron interne à une application : elle organise les dépendances autour du domaine. Les microservices sont un style de distribution entre plusieurs applications. On peut faire de l’hexagonal dans un monolithe, et on devrait le faire dans chaque microservice. Les deux se complètent.

Quand préférer la communication par événements à un appel direct ?
Privilégiez les événements quand l’émetteur n’a pas besoin d’une réponse immédiate et ne doit pas dépendre de la disponibilité du destinataire. Gardez l’appel synchrone quand vous avez impérativement besoin d’un résultat pour continuer, par exemple une vérification d’autorisation avant d’afficher une page.

CQRS impose-t-il deux bases de données ?
Non. CQRS sépare les modèles de lecture et d’écriture, pas nécessairement les bases. On peut commencer avec une seule base et deux ensembles d’objets, puis n’introduire une base de lecture dédiée que si la charge le justifie.

Une saga remplace-t-elle une transaction de base de données ?
Elle en offre une version distribuée et plus faible. Une transaction classique garantit l’atomicité immédiate ; une saga garantit une cohérence à terme via des compensations. On ne l’utilise que lorsque l’opération franchit des frontières de services, jamais à l’intérieur d’un seul service où une transaction locale suffit.

Ces patterns valent-ils pour une petite équipe ?
Le DDD et l’hexagonal, oui, sans réserve : ils améliorent n’importe quel code. Les microservices, l’event-driven et les sagas, en revanche, ajoutent une complexité opérationnelle réelle. Une petite équipe gagne presque toujours à rester sur un monolithe modulaire bien structuré le plus longtemps possible.

Par où commencer concrètement ?
Par la modélisation du domaine. Tant que vous ne savez pas nommer précisément ce que fait votre logiciel, aucun choix technique ne sera solide. Le premier tutoriel de la série part exactement de là.

Ressources de référence

Mots-clés : architecture logicielle, domain-driven design, architecture hexagonale, microservices, architecture orientée événements, CQRS, event sourcing, pattern saga.

Service ITSkillsCenter

Application mobile Android et iOS

Création d'application mobile Android et iOS. À partir de 350 000 FCFA.

Démarrer mon projet
Publicité