Développement web

Écrire dans le legacy et la cible sans fabriquer deux vérités impossibles à rapprocher

Jérémy Chomel Dawap
  • Publié le : 4 août 2026
  • Temps de lecture : 13 minutes
  1. Définir le contrat temporaire
  2. Nommer une seule autorité
  3. Journaliser l’intention métier
  4. Garantir ordre et idempotence
  5. Choisir entre écriture synchrone, outbox et capture
  6. Traiter les succès partiels
  7. Réconcilier par invariants
  8. Décider les conflits
  9. Neutraliser les effets de bord
  10. Mesurer la divergence
  11. Basculer par cohortes
  12. Appliquer à une commande
  13. Éviter les erreurs fréquentes
  14. Déployer en six semaines
  15. Relier couture et migration
  16. Conclusion : sortir du dual write
Portrait de Jérémy Chomel

Une application legacy accepte une modification client, mais l’écriture vers la cible expire après le timeout. L’utilisateur recommence, le retry réussit côté cible et rejoue aussi la mise à jour historique : deux adresses deviennent actives, un prélèvement part vers la mauvaise et aucun écran ne signale immédiatement le problème.

La douleur apparaît plus tard dans le support, la finance ou l’exploitation, lorsque deux systèmes racontent une histoire différente avec des réponses HTTP pourtant vertes. Deux signaux faibles précèdent l’incident : la file de reprise vieillit et les écarts manuels se concentrent sur une transition de statut précise.

Le vrai enjeu n’est pas de réussir deux appels. Vous allez comprendre comment conserver une intention unique, nommer l’autorité, rendre chaque tentative idempotente, réconcilier des invariants métier et arrêter le dual write dès que la cohorte peut basculer.

Une expertise en développement d’applications métier transforme cette discipline en trajectoire mesurable. La refonte de logiciel métier transfère ensuite progressivement les responsabilités sans perdre preuve, service ni capacité de rollback.

Définir le dual write comme un contrat temporaire

Le dual write signifie qu’une intention métier doit produire deux représentations cohérentes pendant une période bornée. Cette période possède une population, un owner, une date de fin, des invariants, un SLO de divergence et une procédure de retour.

Fixer les critères d’entrée et de sortie avant la première écriture

Le contrat nomme objets concernés, attributs dupliqués, sens de propagation, latence acceptable et cas exclus. Une commande financière critique ne partage pas forcément la même trajectoire qu’une préférence d’affichage facilement recalculable.

La sortie exige une cible complète, réconciliée et capable de soutenir le run. Si la réconciliation reste au-dessus du seuil accepté, alors la cohorte ne grandit pas ; en revanche, le calendrier projet ne justifie jamais d’effacer l’écart.

Le coût caché est suivi dès le départ : temps de support, stockage des preuves, files, astreinte, corrections et gel fonctionnel. Sans ce coût complet, une transition provisoire devient une architecture permanente par inertie.

Nommer une seule autorité pour chaque décision

Deux systèmes peuvent stocker la même donnée sans avoir le droit de la décider. L’autorité porte par attribut et phase : le legacy peut rester maître du statut tandis que la cible devient maître de la préférence de contact.

Distinguer source de vérité et copie opérationnelle

L’écriture entre d’abord dans l’autorité, qui valide les invariants et attribue une version métier. La seconde représentation reçoit cette décision avec identifiant, version, auteur et instant d’effet ; elle ne réinterprète pas la règle.

Une matrice date les transferts d’autorité par cohorte. Elle interdit qu’un écran legacy et un écran cible modifient simultanément le même attribut, même si les deux formulaires restent visibles pendant la transition.

Contre-intuitivement, rendre la cible prioritaire trop tôt augmente le risque : une donnée correcte mais non propagée vers le legacy peut déclencher un effet de bord historique. L’autorité change seulement lorsque tous les consommateurs critiques suivent la nouvelle décision.

Journaliser l’intention avant de distribuer les écritures

Un appel applicatif ne doit pas perdre l’intention entre transaction locale et message sortant. L’outbox enregistre ensemble mutation autoritaire, event_id, aggregate_id, version, payload minimal et statut de publication.

Faire de l’intention la preuve commune

Le dispatcher lit l’outbox, publie puis marque la livraison sans supprimer la ligne probante. Un crash entre publication et acquittement peut créer un doublon, mais l’idempotence du consommateur empêche une deuxième décision.

L’entrée contient commande métier, version attendue et clé d’idempotence ; la sortie contient décision, nouvelle version et événement. L’owner du domaine garantit le contrat, tandis que le monitoring suit âge de l’outbox, retries et dépendance indisponible.

La rétention couvre au minimum la fenêtre de reprise et d’audit. Une purge ne part qu’après watermark signé par tous les consommateurs requis, afin de préserver la capacité de reconstruire la cible.

Garantir ordre, version et idempotence

Les messages n’arrivent pas toujours dans l’ordre, surtout après un retry ou une partition réseau. Un timestamp ne suffit pas : deux horloges peuvent dériver et deux actions proches porter la même précision.

Versionner chaque agrégat métier

Le producteur incrémente une version par client, commande ou dossier. Le consommateur applique la version suivante, ignore un doublon déjà prouvé et place un saut de séquence en attente plutôt que de deviner l’état manquant.

La clé d’idempotence désigne l’intention, pas l’exécution technique. Un nouveau worker, une nouvelle queue ou un rejeu manuel retrouvent donc le même résultat sans recréer facture, email ou mouvement comptable.

Si la version reçue est plus ancienne, alors elle est ignorée avec preuve ; si une version manque au-delà du délai, alors le runbook demande un snapshot autoritaire. Le « dernier écrit gagne » reste à refuser car il masque le conflit.

Choisir entre écriture synchrone, outbox et capture de changements

Appeler deux bases dans la même requête paraît simple, mais aucune transaction locale ne rend atomiques deux systèmes autonomes. La latence utilisateur et les timeouts deviennent alors des arbitres métier accidentels.

Choisir selon l’autorité et la tolérance au retard

L’outbox convient lorsqu’une application possède l’autorité et peut publier ses décisions. La capture de changements aide une source difficile à modifier, à condition de reconstruire la sémantique et de ne pas traiter chaque colonne comme un événement métier.

Une orchestration synchrone reste utile pour une validation qui doit répondre avant confirmation, mais elle journalise l’intention puis compense le succès partiel. Une écriture directe dans deux bases sans ledger ni retry contrôlé ne franchit pas la porte.

Le choix est testé sur panne réelle : base cible lente, broker indisponible, réponse perdue après commit et schéma incompatible. La solution retenue prouve ce qui est confirmé à l’utilisateur dans chacun de ces scénarios.

Dans un backend Symfony, Doctrine enregistre mutation et outbox dans la même transaction, puis Messenger exécute le worker. Les tests de contrat, la CI et la QA vérifient performance, cache, invalidation et rollback avant chaque déploiement.

Traiter chaque succès partiel comme un état explicite

Un succès dans le legacy suivi d’un échec cible ne se corrige pas forcément par annulation. Le legacy peut avoir envoyé une notification ou déclenché un calcul irréversible ; la compensation doit respecter le domaine.

Décider entre retry, compensation et quarantaine

Une erreur transitoire déclenche un retry borné avec backoff et même identifiant. Une violation métier va en quarantaine avec raison, valeur exposée et owner ; une erreur définitive ferme la tentative et appelle une compensation approuvée.

La file morte n’est pas une fin de traitement. Elle conserve payload utile, première erreur, nombre d’essais, version de code et commande de reprise, puis alerte avant que son âge dépasse le SLO.

Par exemple, si 0,2 % des écritures restent divergentes plus de dix minutes, alors l’extension de cohorte s’arrête et l’équipe corrige la première cause. En revanche, augmenter indéfiniment les retries ne restaure pas un contrat cassé.

Réconcilier des invariants métier, pas seulement des colonnes

Deux JSON identiques ne prouvent pas une cohérence fonctionnelle, et deux structures différentes peuvent représenter la même décision. La réconciliation compare les invariants qui conditionnent facture, livraison, droit ou promesse client.

Produire un écart actionnable et reproductible

Chaque contrôle porte aggregate_id, version attendue, valeurs autoritaires, projection cible, règle, sévérité et première divergence. Un checksum accélère le tri, mais le dossier conserve les champs nécessaires à l’explication.

Les invariants incluent somme des lignes égale au total, transition de statut autorisée, unicité d’un identifiant actif, conservation de quantité et absence d’effet de bord en double. Ils sont versionnés avec le contrat de migration.

La réconciliation tourne en continu pour les nouveaux événements et en lot pour la couverture historique. Le ratio seul est insuffisant : une divergence sur mille préférences marketing ne vaut pas une facture sur mille.

Décider les conflits avec une politique de domaine

Un conflit révèle deux décisions concurrentes ou une propagation incomplète. Copier la valeur la plus récente efface contexte, autorité et parfois une validation réglementaire.

Classifier avant de corriger

Le moteur distingue doublon, retard, ordre inversé, mapping, autorité contestée et modification réellement concurrente. Chaque classe possède une action autorisée : ignorer, rejouer, recalculer, demander une décision humaine ou bloquer.

Une correction humaine crée un nouvel événement portant auteur, motif, preuves et version de départ. Elle ne modifie jamais directement la base cible, faute de quoi la prochaine propagation reproduirait l’écart.

Le responsable métier arbitre les conflits de sens ; l’équipe technique restaure la mécanique. Ce partage évite qu’un développeur choisisse seul quelle adresse, quel montant ou quel statut devient juridiquement opposable.

Neutraliser emails, paiements et autres effets de bord

Le plus grand danger n’est pas toujours une donnée différente, mais un effet exécuté deux fois. Email, prélèvement, réservation de stock et document comptable exigent leur propre clé d’idempotence.

Séparer projection et commande irréversible

La cible en mode miroir calcule son résultat mais n’émet aucun effet externe. Un port dédié remplace email, PSP ou transporteur par un sink de preuve qui conserve la commande sans l’exécuter.

Après bascule, l’autorisation s’inverse : seule la cible publie l’effet, tandis que le legacy reçoit une projection compatible. Un kill switch peut revenir au producteur précédent sans perdre les événements déjà confirmés.

Un test rejoue le même événement dix fois et vérifie une seule facture, un seul paiement puis une seule notification. Le compteur d’appels ne suffit pas ; la preuve contrôle aussi la clé reconnue par le fournisseur externe.

Mesurer divergence, retard et capacité de reprise

Un tableau de bord global peut rester vert tandis qu’une cohorte stratégique diverge. Les mesures sont segmentées par domaine, version, route, cohorte, cause et valeur métier exposée.

Relier métriques et décision de bascule

Suivez âge du plus ancien événement, taux de divergence pondéré, délai de convergence, quarantaine, retries, corrections et succès du dernier exercice de rollback. Les percentiles révèlent les longues traînes cachées par une moyenne.

Une alerte fournit corrélation, invariant cassé, première version, population, valeur exposée et owner. Elle propose gel de cohorte, rejeu ciblé ou retour d’autorité au lieu d’un redémarrage global.

L’instrumentation accepte en entrée event_id, aggregate_id et policy_version ; sa sortie relie traces, logs et métriques au même verdict. Deux paragraphes de runbook précisent dépendances, seuils et repli avant l’ouverture.

Basculer par cohortes avec un rollback exercé

La cohorte réduit le rayon d’impact : équipe interne, tenant pilote, type de commande ou zone. Elle doit cependant représenter les cas difficiles, faute de quoi le succès ne prédit pas le comportement du reste.

Définir une porte quantitative et qualitative

La porte exige couverture de réconciliation, absence de divergence critique, SLO de convergence, astreinte formée et rollback chronométré. Un seuil numérique sans revue des invariants prioritaires donnerait une confiance artificielle.

Le rollback restaure l’autorité, arrête le producteur cible et rejoue vers le legacy les décisions déjà engagées. Il ne consiste pas à redéployer une ancienne version en espérant que les données se réalignent seules.

La cohorte suivante n’ouvre qu’après une durée d’observation couvrant le cycle métier. Une commande hebdomadaire ou une clôture mensuelle ne se valide pas avec deux heures de trafic nominal.

Dans quel cas appliquer la méthode : migrer une commande

Un distributeur migre la gestion de commande sans interrompre l’ERP historique. Le legacy garde l’autorité sur validation et facturation, tandis que la cible calcule une projection de statut et prépare la future orchestration.

Commencer par une cohorte sans paiement différé

Les commandes internes d’une région entrent dans l’outbox avec order_id, version et total. La cible projette lignes, livraison et statut ; ses emails et appels transporteur restent neutralisés.

Le seuil de passage exige zéro divergence de total sur dix mille commandes, moins de cinq minutes de convergence au percentile 99 et aucun effet externe en double. Une erreur critique gèle immédiatement la cohorte.

Transférer ensuite l’autorité et prouver la sortie

Après deux cycles complets, la cible devient autoritaire pour la préparation. Le legacy reçoit le statut versionné et conserve la facturation ; le kill switch replace l’ancien orchestrateur si le SLO se dégrade.

La réconciliation compare total, quantités, transitions, expéditions et factures. Lorsque le legacy n’a plus aucun lecteur décisionnel sur la préparation, le dual write de ce périmètre est supprimé au lieu de rester dormant.

Le bilan mesure incidents, temps de support, délai de convergence et coût d’exploitation. La modernisation progresse seulement si ce coût baisse avec la dette de dépendance historique.

Éviter les erreurs fréquentes du dual write

Les erreurs classiques sont deux autorités, une écriture synchrone sans journal, des retries sans idempotence, une réconciliation de colonnes, un timestamp comme règle de conflit et une file morte sans owner.

Refuser le provisoire sans expiration

Un dual write sans date de sortie acquiert des consommateurs, des tableaux de bord et des corrections manuelles. Plus il dure, plus retirer une branche devient risqué même lorsqu’elle ne devrait plus décider.

Réparer directement la cible détruit la reproductibilité. La correction repasse par une commande autoritaire ou par un événement de remédiation afin que chaque projection converge avec la même histoire.

Enfin, comparer uniquement les succès HTTP ignore pertes, inversions et effets doubles. La preuve suit intention, version, invariant et résultat métier jusque chez la dépendance externe.

Plan d’action : sécuriser la transition en six semaines

Le pilote commence sur un domaine dont les invariants sont connus et dont le rollback reste praticable. Il vise une chaîne observable avant toute hausse de trafic.

Semaines 1 et 2 : autorité, invariants et baseline

Cartographiez writers, readers, effets, versions et owners. Mesurez écarts actuels, corrections, latence et coût support, puis sélectionnez une cohorte contenant au moins un cas de retry et un conflit.

La sortie comprend matrice d’autorité, contrat d’événement, invariants pondérés et critères d’arrêt. À refuser : une donnée dont aucune équipe ne peut décider la valeur opposable.

Semaines 3 et 4 : journal, projection et réconciliation

Implémentez outbox, version, idempotence, quarantaine et contrôles continus, puis injectez timeouts, doublons, ordre inversé, schéma invalide et indisponibilité de la cible sur une cohorte de recette représentative.

Le monitoring doit retrouver chaque écart depuis l’alerte jusqu’à l’intention source, tandis que le runbook exerce retry, compensation, repli et réparation par nouvel événement avec un owner identifié.

Semaines 5 et 6 : cohorte, bascule et suppression

Ouvrez la cohorte en mode miroir, puis transférez une responsabilité après les seuils. Chronométrez le rollback et vérifiez les décisions prises pendant son exécution.

La porte finale exige invariants critiques verts, délai de convergence tenu, aucun effet double et astreinte autonome. Planifiez immédiatement la suppression de la branche historique devenue inutile.

  1. D’abord, nommer une autorité et un invariant par décision avant de propager la moindre écriture vers la cible.
  2. Ensuite, journaliser l’intention, versionner l’agrégat et rendre chaque consommation idempotente malgré retries et ordre variable.
  3. Puis, réconcilier en continu, pondérer les écarts par impact et bloquer l’extension dès que le SLO dérive.
  4. À faire enfin : transférer l’autorité par cohorte, exercer le rollback et supprimer chaque double écriture arrivée à expiration.

Guides complémentaires : relier couture, données et migration

Le dual write reste une conséquence du découpage. Une frontière mal choisie multiplie les transactions distribuées, tandis qu’une reprise de données mal prouvée rend la réconciliation impossible.

Construire une trajectoire de modernisation cohérente

La méthode sur les coutures d’une application legacy aide à choisir les frontières. La matrice de modernisation legacy décide réparer, encapsuler, extraire ou remplacer.

La démarche de reprise de données d’un logiciel métier apporte mapping, répétitions et contrôles de bascule. Le shadow traffic compare la cible avant qu’elle ne produise des effets.

Ensemble, ces ressources réduisent le nombre d’écritures partagées, prouvent les projections et donnent une séquence de transfert mesurable plutôt qu’une longue coexistence indécidable entre deux systèmes devenus également difficiles à retirer.

  • À prioriser : statuts, montants, stocks, droits et commandes dont une divergence déclenche un effet externe coûteux.
  • À surveiller : outbox vieillissante, séquences manquantes, quarantaine, corrections directes et cohortes qui ne progressent plus.
  • À refuser : deux autorités, une compensation implicite ou un dual write sans owner, seuil de sortie et rollback exercé.

Conclusion : réussir le dual write signifie pouvoir le supprimer

Le dual write ne crée pas de sécurité par lui-même. Il augmente temporairement le nombre d’états possibles ; seule une intention journalisée, une autorité et une réconciliation métier transforment cette complexité en transition contrôlée.

Outbox, versions et idempotence protègent la mécanique, tandis que les invariants, owners et seuils protègent la décision. Les effets externes restent neutralisés tant que la cible n’est pas autoritaire.

Le succès se voit quand divergence, délai de convergence et charge support diminuent au fil des cohortes, puis quand la branche historique disparaît sans perte de preuve ni de service.

Pour structurer cette trajectoire, l’accompagnement de notre expertise en refonte de logiciels métier relie architecture, migration de données, observabilité et run afin de transférer chaque responsabilité avec une sortie et un retour praticables.

Portrait de Jérémy Chomel

Vous avez un projet de
développement sur mesure ?

Dawap transforme ce besoin en périmètre livrable, architecture maintenable et trajectoire de mise en production adaptée à vos contraintes.

Besoin d’échanger sur votre projet ? Planifier un rendez-vous

Articles recommandés

Trafic applicatif dupliqué vers legacy et cible isolée avec corrélation, comparaison des décisions et contrôle des effets de bord Développement web Shadow traffic : comparer une cible sans effet de bord Lire l'article
  • 3 août 2026
  • Lecture ~13 min

Le shadow traffic ne prouve rien si la cible envoie emails, paiements ou écritures, ou si les écarts non déterministes masquent le métier. Cette méthode duplique, corrèle, neutralise les effets, compare les décisions par scénario et transforme la parité en seuil de bascule réversible, sans ralentir le legacy autoritaire.

Cartographie des coutures d’une application legacy entre dépendances et transactions Développement web Trouver les vraies coutures d’un legacy Lire l'article
  • 2 août 2026
  • Lecture ~12 min

Découper un legacy au mauvais endroit multiplie appels, doubles écritures et incidents. Cette méthode observe dépendances, transactions, propriété des données, effets de bord et fréquence de changement pour choisir une frontière réversible, la caractériser, l’ouvrir puis retirer une ancienne voie sur preuve.

Matrice de décision pour moderniser progressivement une application legacy Développement web Choisir la bonne trajectoire legacy Lire l'article
  • 1er août 2026
  • Lecture ~12 min

Réécrire tout un système historique impose le risque maximal à des capacités très différentes. Cette matrice relie valeur, obsolescence, données, couplage et réversibilité pour décider où stabiliser, encapsuler, étrangler ou remplacer, avec des preuves de parité et de retrait à chaque étape de livraison.

Plan de reprise de données d’un logiciel métier Développement web Reprise de données métier : réussir une bascule fiable Lire l'article
  • 21 juillet 2026
  • Lecture ~8 min

Une migration de données n’est pas validée par un simple nombre de lignes. Ce guide construit inventaire, éligibilité, correspondances, identités, transformations, lots, quarantaines et contrôles métier. Il organise répétitions chronométrées, delta, gel, bascule, rapprochement, retour arrière et archive probatoire pour démarrer avec des données explicables.