Quand un système historique commence à freiner le produit, la réécriture complète revient vite dans la discussion. Repartir sur un modèle plus clair, une API moderne et des frontières mieux définies a quelque chose de séduisant.

Pendant ce temps, le legacy continue à porter les usages, les données et les règles métier dont l’entreprise a besoin tous les jours. La difficulté est de faire évoluer cette dépendance alors que le système reste en fonctionnement.

Sur une mission de plus de deux ans, j’ai contribué à la conception, au développement et à l’exploitation d’une plateforme de référentiel critique. Elle combinait une API moderne et un pipeline CDC temps réel pour moderniser progressivement l’existant, avec plusieurs centaines de millions de données traitées et synchronisées. Ce contexte m’a amené à regarder de près ce qui se passe entre le système de départ et la cible : la coexistence, les reprises, le diagnostic et la transmission à l’équipe.

Décrire les étapes intermédiaires

Une architecture cible peut être très propre sur un diagramme. Le projet réel passe une bonne partie de sa vie dans des états intermédiaires : l’ancien système fonctionne toujours, le nouveau commence à servir certains usages et les consommateurs n’avancent pas tous au même rythme.

Avant de choisir une stack, je préfère donc comprendre la trajectoire. Quel usage veut-on découpler en premier ? Qui reste responsable de la donnée pendant cette étape ? Quel décalage un consommateur peut-il accepter ? Comment saura-t-on qu’on peut basculer le suivant ?

La question de la responsabilité est particulièrement importante. Si les deux systèmes peuvent modifier la même donnée, il faut décider comment leurs écritures s’articulent et comment résoudre un conflit. Une synchronisation entre deux bases ne prend pas cette décision à notre place.

Pour chaque étape, on doit pouvoir expliquer où la donnée fait autorité, par quel chemin elle arrive chez le consommateur et ce qui permet de poursuivre la migration. Sans ça, « progressivement » peut simplement devenir une coexistence sans fin.

Utiliser le CDC comme une voie de transition

Le CDC, pour change data capture, consiste à capter les changements de données pour permettre à d’autres systèmes de les traiter. Dans le contexte de cette mission, il alimentait une plateforme moderne à partir de l’existant.

Le principe permet de continuer à faire vivre le système historique tout en construisant un nouveau modèle de lecture et une API de référentiel. Ce modèle peut devenir utile avant que l’ensemble du legacy ait été remplacé.

Le legacy alimente le CDC, puis un modèle métier exposé par une API de référentiel aux consommateurs. Certains usages restent sur le legacy pendant la transition.
Schéma de principe d’une transition par CDC. Il montre le chemin de la donnée, sans décrire une architecture client détaillée.

Il faut aussi faire attention à ce que transporte le flux. Une modification capturée en base décrit un changement de donnée. Elle n’exprime pas forcément l’intention métier qui l’a provoqué. La transformation vers un modèle plus clair reste un travail de conception, avec des règles à expliciter et des cas limites à traiter.

Le CDC déplace donc une partie de la complexité vers la synchronisation et son exploitation. L’API moderne doit pouvoir servir ses usages avec ce que le pipeline a réellement traité, y compris lorsqu’il a pris du retard.

Séparer doublons, ordre et effets métier

Dans un pipeline asynchrone, les doublons, les dépendances entre entités et les reprises historiques sont des contraintes à examiner dès la conception. Les exemples qui suivent sont génériques : ils expliquent les mécanismes, sans raconter un incident de cette mission.

Un même changement peut être présenté plusieurs fois au traitement, selon les garanties de livraison et les conditions de reprise. La documentation de Debezium décrit notamment les doublons possibles lors de certaines récupérations après panne. Il faut donc connaître les garanties de son pipeline et définir le comportement attendu lorsqu’un message est retraité.

L’idempotence permet de refaire une opération sans multiplier son effet. Mais elle ne règle pas, à elle seule, la question de l’ordre.

Prenons une projection qui a déjà appliqué la version 13 d’une entité. Réappliquer la version 13 peut être sans effet supplémentaire. Recevoir ensuite la version 12 pose un autre problème : si le traitement écrase simplement l’état courant, il peut rétablir une ancienne valeur. La règle de version, l’ordre de traitement ou une autre stratégie de résolution doit empêcher cette régression.

Avec Kafka, l’ordre des enregistrements est préservé à l’intérieur d’une partition. Il ne faut pas en déduire un ordre global entre plusieurs partitions, ni un ordre métier automatiquement conservé à travers des chemins de retry différents. Le choix de la clé et le traitement des dépendances restent importants. C’est la portée de la garantie décrite dans l’introduction officielle de Kafka.

Il faut enfin regarder les effets qui sortent de la projection : un appel à un autre service, une notification ou une écriture supplémentaire. Un replay peut reconstruire correctement une donnée et déclencher une deuxième fois une action qu’on ne voulait pas répéter. La sûreté de la reprise se vérifie sur tout ce chemin.

Concevoir le chemin de reprise

Les retry, la dead letter et le replay faisaient partie des sujets de résilience traités sur cette plateforme. Pour ce type de système, je les considère comme une partie du fonctionnement attendu en production.

Un retry est utile lorsqu’un problème est temporaire. Réessayer indéfiniment une donnée que le traitement ne sait pas interpréter ne fait que retarder la prise en charge du problème. Il faut pouvoir distinguer ce qui peut être retenté de ce qui demande une correction ou une décision.

La dead letter permet d’isoler des messages en échec, mais la donnée concernée reste à traiter. Il faut conserver assez de contexte pour comprendre l’erreur, identifier qui la prend en charge et prévoir ce qui se passe après la correction. Si d’autres données dépendent de celle qui a été isolée, la suite du flux mérite aussi une décision explicite.

Le replay a une portée différente. Il peut servir à retraiter un périmètre après une correction ou à reconstruire une projection. Avant de le lancer, je veux savoir quel historique est encore disponible, quel code va l’interpréter, quels effets secondaires peuvent se produire et comment vérifier le résultat. Il faut aussi maîtriser la charge ajoutée au flux courant.

Même quand le transport propose de fortes garanties, les écritures et les effets dans des systèmes externes demandent leur propre coordination. La documentation de conception de Kafka précise cette limite pour l’exactly-once. Une garantie locale ne suffit pas à conclure que toute la chaîne peut être rejouée sans risque.

Vérifier la convergence, au-delà du service « up »

Un service disponible peut exposer des données anciennes, incomplètes ou incorrectes. De la même manière, un retard de consommation revenu à zéro ne prouve pas, à lui seul, que la projection est correcte.

L’observabilité doit aider à répondre à des questions d’exploitation : où le flux s’est-il arrêté ? Quel périmètre est touché ? Est-ce une erreur ponctuelle ou une catégorie de données qui échoue ? Qu’est-ce qui reste en attente de remédiation ?

La convergence demande en plus un critère métier. On ne compare pas nécessairement deux bases à l’identique si le modèle cible transforme, filtre ou regroupe les données. Il faut définir ce qui doit correspondre pour l’usage concerné : présence des entités attendues, valeurs de référence, relations, prise en compte des suppressions ou fraîcheur acceptable.

Sur un système qui continue à recevoir des écritures, cette comparaison doit aussi tenir compte du moment auquel on observe chaque côté. Un écart peut venir d’une transformation incorrecte, d’un retard normal ou d’une donnée encore en erreur. La manière de les distinguer fait partie du dispositif de migration.

Ces contrôles sont un coût supplémentaire. Ils donnent surtout à l’équipe un moyen de décider si une reprise a fonctionné et si une nouvelle étape de bascule est raisonnable.

Préparer une bascule limitée et un retour réaliste

Découper la migration par usage réduit le périmètre de chaque décision. On peut faire passer un consommateur par l’API moderne, vérifier son comportement, puis poursuivre avec le suivant.

Pour autant, une migration progressive ne garantit pas un rollback global. Revenir à l’ancien chemin de lecture peut être possible tant qu’il reste disponible et compatible avec les attentes du consommateur. Si des écritures ont changé de système ou que des effets métier ont déjà été déclenchés, revenir en arrière peut demander une réconciliation des données ou des actions de compensation.

Je préfère expliciter cette limite avant la bascule : ce qu’on peut arrêter, ce qu’on peut rediriger, les données qu’il faudrait remettre en cohérence et la personne qui peut prendre la décision. « Réversible » n’a de valeur que si on sait de quelle étape et de quels effets on parle.

Les runbooks et le handover entrent dans le même raisonnement. Le chemin de reprise doit pouvoir être utilisé par l’équipe, avec les informations nécessaires au diagnostic et à la vérification. Si seule la personne qui a conçu le pipeline sait le remettre en fonctionnement, une dépendance importante reste entière.

Assumer le coût de coexistence et préparer sa fin

Pendant la transition, il faut maintenir l’ancien et le nouveau, leurs interfaces et la synchronisation entre les deux. Il faut aussi suivre les écarts, adapter les consommateurs et garder une équipe capable d’opérer l’ensemble.

Ce coût peut être justifié lorsque le système est critique, volumineux, difficile à arrêter ou entouré de nombreux usages. Il peut aussi devenir une nouvelle dette si personne ne sait quelles dépendances doivent disparaître et dans quelles conditions.

La progression se mesure donc aussi par ce qu’on peut retirer : un accès direct au legacy, une règle dupliquée, une interface devenue inutile ou une procédure dont l’équipe n’a plus besoin. Chaque retrait doit rester cohérent avec les usages encore présents.

Sur un système plus simple, avec une fenêtre d’arrêt acceptable et un périmètre bien maîtrisé, une migration directe peut être plus rationnelle. Le CDC et l’event-driven ajoutent de l’infrastructure, du diagnostic et des mécanismes de reprise. Il faut comparer ce coût au risque de transition qu’ils permettent réellement de réduire.

Ce que je retiens

La plateforme sur laquelle j’ai travaillé a posé une base moderne pour diminuer progressivement la dépendance au legacy, tout en traitant et synchronisant plusieurs centaines de millions de données. Ce résultat reste une étape de modernisation, pas la preuve que toute la plateforme historique a disparu.

L’architecture cible compte. Je veux aussi pouvoir expliquer comment chaque état intermédiaire fonctionne, comment l’équipe reprend les données en erreur et sur quelle preuve elle s’appuie pour poursuivre. C’est ce qui rend la trajectoire utilisable au-delà du diagramme.

Travail lié : API de référentiel et synchronisation temps réel, l’étude de cas publique associée à cette expérience.