Découper le chantier en lots
Sur Le Chantier, personne ne confie l'immeuble entier à une seule entreprise. Le gros œuvre va à l'une, la plomberie à une autre, l'électricité à une troisième, les menuiseries à une quatrième. Chaque lot a son contrat, son planning, son chef d'équipe.
Le découpage a une raison précise. Si le carreleur prend trois semaines de retard, l'électricien continue de tirer ses câbles. Et si le maître d'ouvrage change le modèle de carrelage, il ne rouvre pas le marché de la charpente.
Il a aussi un prix, que tout le monde connaît sur un chantier et que beaucoup oublient en informatique : quelqu'un doit coordonner. Les lots se croisent, les réservations doivent tomber au bon endroit, et une réunion hebdomadaire ne suffit pas toujours à rattraper une incompréhension entre deux entreprises.
Les microservices, c'est ce découpage en lots. L'event sourcing, c'est le journal de chantier, qui consigne chaque événement dans l'ordre où il s'est produit. CQRS, c'est la différence entre ce journal et le tableau d'avancement affiché en base vie.
Trois patterns qu'on cite en réunion pour avoir l'air à jour. Trois patterns qui coûtent très cher quand on les sort sans raison.
La bonne question n'est jamais « est-ce que c'est moderne ». Elle est : « est-ce que le problème que j'ai est celui que ce pattern résout ».
Trois patterns, trois questions différentes
Avant d'entrer dans le détail, il faut poser une chose que les présentations mélangent souvent : ces trois patterns sont indépendants. On peut découper en microservices sans event sourcing, faire de l'event sourcing sans CQRS, appliquer CQRS dans un monolithe.
Ils ne répondent d'ailleurs pas à la même question. Le premier porte sur le découpage et le déploiement du système, le deuxième sur la façon dont on stocke la vérité, le troisième sur la séparation des chemins d'accès à cette vérité.
| Pattern | La question à laquelle il répond | Le problème qu'il traite | Ce qu'il coûte |
|---|---|---|---|
| Microservices | Comment je découpe et je déploie le système ? | Un bloc unique qu'on ne peut ni mettre à jour ni dimensionner par morceaux | Supervision, tests d'intégration, architecture répartie |
| Event sourcing | Qu'est-ce que je stocke comme vérité ? | L'état courant écrase l'histoire, donc l'audit et la correction deviennent impossibles | Volume de stockage, lecture d'un état, montée en compétence de l'équipe |
| CQRS | Comment je sépare l'écriture de la lecture ? | Un modèle unique qui sert mal deux usages opposés | Deux modèles à maintenir, cohérence à terme à assumer |
Ils se croisent souvent en pratique, pour une raison simple : chacun crée un problème que le suivant sait traiter. Le découpage en services rend la donnée répartie, l'event sourcing rend la lecture pénible, CQRS répare la lecture au prix d'un décalage dans le temps.
Choisir l'un des trois, c'est accepter le problème qu'il crée. Les adopter tous les trois d'un coup, c'est accepter les trois problèmes avant d'avoir vérifié qu'on avait les trois besoins.
Microservices : ce que le découpage résout, et ce qu'il coûte
L'équivalent du monolithe, sur Le Chantier, c'est l'entreprise générale qui fait tout, avec une seule équipe et un seul planning. Tant que le chantier est petit, c'est le choix le plus efficace : aucune coordination inter-entreprises, aucune interface contractuelle à négocier.
Un monolithe logiciel, c'est la même chose. Une seule application, un seul processus, un seul déploiement. Toutes les fonctions cohabitent, s'appellent en mémoire, partagent la même base. C'est simple, rapide à démarrer, et ça marche très bien longtemps.
Quatre douleurs finissent par apparaître, toujours les mêmes.
Une exception non rattrapée tue tout le processus. Le module d'export PDF plante sur un caractère exotique, et la prise de commande s'arrête en même temps que lui, parce qu'ils partagent le même processus.
Une mise à jour impacte tout. Corriger une virgule dans le calcul de TVA impose de redéployer l'application entière, donc de rejouer la campagne de tests complète et d'immobiliser des fonctions qui n'ont pas bougé d'une ligne.
Une seule plateforme technique pour tout le système. Le traitement d'images gagnerait à être écrit dans un autre langage, la recherche aussi. Impossible : tout vit dans le même processus, donc tout parle la même langue.
Les ressources sont dimensionnées pour le pire cas. Le composant le plus gourmand impose sa taille de machine à tous les autres, et on paie cette taille pour l'ensemble, y compris pour les modules qui tournent trois fois par jour.
Le découpage en services traite ces quatre points, un par un. Un service qui tombe ne tombe que lui. Chaque service se déploie seul, sur son propre rythme. Chaque service peut avoir sa stack, tant que son contrat d'interface reste stable. Et on ne multiplie les instances que du service réellement sous charge.
2 instances"] fact["Service Facturation
2 instances"] search["Service Recherche
8 instances"] notif["Service Notifications
1 instance"] bus["File de messages"] dbc[("Base Commandes")] dbf[("Base Facturation")] idx[("Index de recherche")] client --> gw gw --> cmd gw --> fact gw --> search cmd --> dbc fact --> dbf search --> idx cmd -- "Commande validée" --> bus bus --> fact bus --> notif classDef io fill:#ede9e1,stroke:#b5651d,color:#1a1a1a classDef svc fill:#d89253,stroke:#8a4d16,color:#1a1a1a classDef hot fill:#b5651d,stroke:#8a4d16,color:#f5f2ec classDef store fill:#f5f2ec,stroke:#5f5e5a,color:#1a1a1a class client,gw io class cmd,fact,notif svc class search hot class bus,dbc,dbf,idx store
Le schéma dit deux choses importantes. Le service Recherche tourne en huit exemplaires parce que c'est lui qui prend la charge, et lui seul. Et chaque service possède ses données : si deux services écrivent dans la même table, tu n'as pas deux services, tu as deux moitiés du même service avec du réseau au milieu.
Un microservice ne se définit pas par sa taille en lignes de code, mais par sa frontière métier et par son autonomie de déploiement. Si tu ne peux pas le livrer seul, ce n'en est pas un.
Maintenant le prix, dit franchement.
La supervision devient un métier. Une requête traverse cinq services, et sans journalisation centralisée ni identifiant de corrélation, tu passes tes nuits d'incident à recoller des horodatages. C'est le sujet du billet sur la messagerie inter-services et la supervision, et ce n'est pas une option.
Les tests changent de nature. Chaque service est plus facile à tester unitairement, mais le comportement du système ne se voit qu'à l'intégration, avec des versions de contrats qui ne bougent pas toutes en même temps.
L'architecture devient répartie, avec tout ce que ça implique : latence réseau à chaque appel, pannes partielles à gérer côté appelant, transactions qui ne peuvent plus être atomiques d'un bout à l'autre. L'isolation des pannes n'est acquise que si l'appelant sait se comporter quand l'appelé ne répond pas. Sinon, tu as juste déplacé le crash.
Event sourcing : stocker les faits, pas l'état
Sur Le Chantier, le conducteur de travaux tient un journal. Livraison de béton le 12 à 7 h 40, coulage du plancher haut le 12 à 14 h, intempéries le 13, reprise le 15. Rien n'est effacé, on ajoute des lignes.
Deux ans plus tard, quand une fissure apparaît, ce journal est la seule chose qui permette de savoir pourquoi le mur est ce qu'il est. Un tableau qui n'afficherait que « plancher haut : coulé » ne dirait rien des conditions dans lesquelles ce plancher a été coulé.
L'event sourcing applique ce principe au stockage. On ne conserve pas l'état courant d'une entité, on conserve la suite ordonnée et immuable des événements qui l'ont produit. L'état courant se recalcule en rejouant ces événements depuis le début.
L'analogie la plus parlante reste le relevé bancaire. Ta banque ne stocke pas seulement « solde : 1 240 € ». Elle stocke chaque opération, et le solde est une conséquence. Si elle ne gardait que le solde, elle ne pourrait ni te le justifier, ni corriger une erreur de saisie sans en fabriquer une autre.
La différence tient en deux instructions.
-- Modèle par état courant : la valeur précédente est définitivement perdue
UPDATE commande
SET statut = 'ANNULEE', montant_total = 0
WHERE id = 4711;
-- Modèle par événements : on ajoute un fait, on n'écrase rien
INSERT INTO evenements (flux_id, numero, type, donnees, horodatage)
VALUES ('commande-4711', 7, 'CommandeAnnulee',
'{"motif":"rupture de stock","par":"agent-32"}', now());
Les événements portent des noms au passé, parce qu'ils décrivent des faits accomplis : CommandeCreee, LigneAjoutee, PaiementAccepte, CommandeAnnulee. On ne les modifie jamais. Une erreur se corrige avec un nouvel événement, exactement comme une écriture comptable se contre-passe.
Reconstruire l'état devient une boucle, et le modèle d'écriture s'en trouve simplifié.
Commande commande = new Commande();
for (Evenement e : journal.lire("commande-4711")) {
commande.appliquer(e);
}
Cette boucle est aussi le premier problème. Rejouer sept événements est instantané, en rejouer quatre cent mille ne l'est pas. La parade classique s'appelle l'instantané (snapshot) : on photographie l'état à l'événement 5 000, et on ne rejoue plus que ce qui suit.
| Critère | Journal d'événements | État courant |
|---|---|---|
| Historique et audit | Complet par construction, sans table d'historique à maintenir | À reconstituer, souvent partiellement |
| Correction d'une erreur | Un événement compensatoire, la trace reste | Un UPDATE qui efface le contexte de la faute |
| Modèle d'écriture | Simple : on ajoute, on ne verrouille presque rien | Simple aussi, mais concurrent sur les mêmes lignes |
| Lecture d'un état | Coûteuse, il faut rejouer ou photographier | Immédiate, c'est le contenu de la ligne |
| Requête transverse | Pas de vue unifiée native, rien de simple pour « toutes les commandes en retard » | Une requête SQL ordinaire |
| Volume de stockage | Croissant et jamais purgé, à planifier dès le premier jour | Stable, proportionnel au nombre d'entités |
L'event sourcing gagne sur l'écriture et sur la traçabilité, et perd sur la lecture. C'est précisément ce déséquilibre que CQRS vient corriger.
Le vrai signal d'usage n'est pas technique, il est métier. Quand l'historique est le produit, comme en comptabilité, en assurance, en logistique ou sur les marchés financiers, l'event sourcing arrête d'être un choix d'architecte pour devenir la traduction fidèle du domaine.
CQRS : séparer le chemin d'écriture du chemin de lecture
Retour en base vie. Personne ne lit le journal de chantier pour savoir où en est le projet. Sur le mur, il y a un tableau d'avancement, mis à jour à partir du journal, lisible en dix secondes.
Deux supports, une seule vérité. Le journal sert à consigner, le tableau sert à consulter. Ils n'ont ni la même forme, ni le même public, ni le même rythme.
CQRS (Command Query Responsibility Segregation) formalise cette séparation. D'un côté le modèle d'écriture, qui traite les commandes, applique les règles métier et rejette ce qui n'est pas valide. De l'autre le modèle de lecture, qui ne fait que répondre à des questions, sur des données déjà mises en forme pour l'écran qui les affiche.
Le terme a été popularisé par Greg Young, à partir du principe de séparation commande/requête formulé par Bertrand Meyer.
traite les commandes"] journal[("Journal d'événements
ajout uniquement")] proj["Projection
consomme les événements"] vue[("Vue de lecture
déjà mise en forme")] query["Chemin de lecture
répond aux requêtes"] ui -- "Créer, modifier, annuler" --> cmd cmd --> journal journal --> proj proj --> vue ui -- "Consulter, rechercher" --> query query --> vue classDef io fill:#ede9e1,stroke:#b5651d,color:#1a1a1a classDef write fill:#b5651d,stroke:#8a4d16,color:#f5f2ec classDef read fill:#d89253,stroke:#8a4d16,color:#1a1a1a classDef store fill:#f5f2ec,stroke:#5f5e5a,color:#1a1a1a class ui io class cmd write class proj,query read class journal,vue store
La pièce centrale, c'est la projection. Elle consomme les événements au fil de l'eau et entretient une vue prête à l'emploi : la liste des commandes du client, avec leur statut et leur montant, dans l'ordre où l'écran les affiche. La question « donne-moi les vingt dernières commandes de ce client » redevient une requête triviale.
Rien n'oblige les deux côtés à utiliser la même technologie. Le journal peut vivre dans une base relationnelle, la vue de lecture dans un index de recherche ou une base document, selon ce que la comparaison SQL / NoSQL donne pour chaque usage.
Il faut assumer la contrepartie, et elle est visible par l'utilisateur : entre l'écriture et la mise à jour de la vue, il s'écoule un délai. Quelques millisecondes en général, davantage sous charge. C'est la cohérence à terme, et elle se conçoit dans l'interface, pas dans le code d'infrastructure.
Le décalage entre l'écriture et la lecture n'est pas un défaut d'implémentation. C'est le contrat de CQRS, et le métier doit le valider avant l'équipe technique.
Dernière précision utile : CQRS n'impose pas deux bases de données. Le premier palier, c'est deux modèles dans le même processus, l'un qui valide et écrit, l'autre qui interroge sans passer par les objets métier. Martin Fowler, qui a documenté le pattern, l'accompagne d'un avertissement explicite sur sa complexité et recommande de le réserver à une portion délimitée du système.
Quand ne pas les utiliser
Sur Le Chantier, découper en lots un abri de jardin serait absurde. Trois entreprises, trois contrats, deux réunions de coordination, pour un ouvrage qu'un artisan seul monte en une semaine.
En informatique, la même absurdité est plus difficile à voir, parce qu'elle se présente avec de bons arguments.
| Pattern | Signe que tu en as besoin | Signe que tu te trompes |
|---|---|---|
| Microservices | Des parties du système ont des profils de charge et des rythmes de livraison franchement différents | Une équipe de quatre personnes, sans chaîne de déploiement ni supervision centralisée |
| Event sourcing | L'historique est une exigence métier : audit réglementaire, comptabilité, réconciliation | Un référentiel qu'on met à jour trois fois par an et que personne n'audite |
| CQRS | Les lectures écrasent les écritures en volume, ou les deux modèles divergent visiblement | Un CRUD où les écrans affichent exactement les champs qu'on saisit |
Trois pièges reviennent plus souvent que les autres.
Le domaine mal connu. Découper avant de comprendre les frontières métier revient à couler des murs porteurs sur un plan provisoire. Martin Fowler défend pour cette raison l'approche « monolithe d'abord » : construire d'un bloc, observer où les coutures apparaissent réellement, puis découper le long de ces coutures.
Le monolithe réparti. Des services séparés sur le papier, mais qui partagent une base de données, s'appellent en chaîne de façon synchrone et se déploient tous ensemble le même soir. Tu as payé le prix du découpage sans en toucher un seul bénéfice.
L'organisation. Un système découpé en huit services demande des équipes capables de les tenir, une chaîne de déploiement qui fonctionne et une astreinte qui sait où regarder. La loi de Conway, énoncée par Melvin Conway en 1967, reste implacable : l'architecture finira par ressembler à l'organisation qui la produit, pas à celle qu'on aurait aimé avoir.
Ces trois patterns résolvent des problèmes d'échelle. Appliqués avant l'échelle, ils ne font que déplacer la complexité du code vers l'exploitation, où elle coûte plus cher.
Scénario B : Prime Video regroupe ce qu'il avait éclaté
En mars 2023, une équipe d'Amazon a publié sur le blog Prime Video Tech un billet intitulé « Scaling up the Prime Video audio/video monitoring service and reducing costs by 90 % ». Il décrit exactement le chemin inverse de celui qu'on vient de dérouler.
Le service en question analyse les flux audio et vidéo diffusés aux clients pour détecter des défauts de qualité, image figée, corruption, désynchronisation du son. Sa première version était une architecture distribuée : une orchestration par machine à états, des composants de détection exécutés en fonctions sans serveur, et un stockage objet pour se passer les images entre étapes.
Deux plafonds ont été atteints, tous les deux décrits dans le billet. Le premier tient au coût et aux limites des transitions d'état de l'orchestrateur, sollicité pour chaque seconde de flux analysée : l'équipe rapporte avoir buté sur un plafond dur autour de 5 % de la charge visée. Le second tient au transport des images, écrites dans le stockage objet puis relues par chaque détecteur, ce qui multipliait les appels facturés.
La refonte n'a pas changé les algorithmes. Elle a regroupé tous les composants dans un processus unique, où les images circulent en mémoire au lieu de transiter par un stockage intermédiaire, et où l'orchestration devient un appel de méthode. Le passage à l'échelle se fait ensuite en dupliquant le service entier, avec une couche légère qui répartit les requêtes entre les copies. Coût d'infrastructure réduit de plus de 90 %, capacité de montée en charge augmentée.
Le goulot d'étranglement n'était pas le traitement lui-même, mais tout ce que l'équipe avait ajouté autour pour le distribuer.
Encore faut-il lire ce billet pour ce qu'il dit : il a beaucoup circulé résumé en « Amazon abandonne les microservices », et le texte ne dit rien de tel.
Ce qui a été regroupé, ce sont les composants internes d'un service précis, pas l'ensemble de Prime Video. Le périmètre est un cas de traitement continu à très haut volume, où le coût de coordination dépasse le coût du calcul. Et la conclusion de l'équipe est explicitement une conclusion de cas particulier : les composants distribués et sans serveur fonctionnent à grande échelle, mais le choix entre eux et un service regroupé se tranche au cas par cas.
C'est la règle de ce billet, prise par l'autre bout. Le découpage n'est pas une valeur, c'est un outil, et un outil se choisit d'après le problème qu'on a devant soi.
Points clés
- Les trois patterns sont indépendants et répondent à trois questions distinctes : comment découper et déployer, quoi stocker comme vérité, comment séparer écriture et lecture.
- Les microservices traitent quatre douleurs réelles du monolithe (panne globale, redéploiement total, plateforme unique, dimensionnement au pire cas) et les remplacent par trois coûts réels : supervision, tests d'intégration, architecture répartie.
- L'event sourcing gagne sur l'écriture et la traçabilité, perd sur la lecture et le volume de stockage. Son déclencheur est métier : l'historique doit être une exigence, pas une curiosité.
- CQRS répare la lecture avec des projections, au prix d'un décalage assumé entre l'écriture et l'affichage. Il commence par deux modèles, pas forcément par deux bases.
- Le cas Prime Video (mars 2023) montre un service à très haut volume où le coût de coordination dépassait le coût du calcul, et où regrouper a réduit la facture de plus de 90 %. Un cas précis, pas un verdict général.
Dans la série
Palier 3 : Conduire le chantier. Domaine : Solutions.
- Précédent : Messagerie inter-services et supervision centralisée
- Suivant : Le document d'architecture : de l'étude de cas au livrable
- Vue d'ensemble : Architecture logicielle : par où commencer
Pour aller plus loin
- Event Sourcing, par Martin Fowler, la description d'origine du pattern
- CQRS, par Martin Fowler, avec l'avertissement d'usage qui va avec
- MonolithFirst, l'argument en faveur du monolithe comme point de départ
- Le pattern d'architecture microservices, par Chris Richardson, et le catalogue de patterns associés
- Billet Prime Video Tech, mars 2023 : « Scaling up the Prime Video audio/video monitoring service and reducing costs by 90 % » (blog primevideotech.com)
- Messagerie inter-services et supervision centralisée, pour ce que les services se disent une fois découpés
- SQL ou NoSQL : choisir son magasin de données, pour le stockage du journal et des vues de lecture