La passerelle entre deux CRM
Synchroniser deux bases de production dans les deux sens, et cloisonner qui voit quoi au niveau de la base elle-même.
Le problème
Le CRM d'audit ne vit pas seul. Un CRM commercial d'une marque sœur partage une grande partie du même modèle métier et doit échanger des dossiers avec lui, en continu : le commercial qualifie un dossier puis l'envoie en audit ; l'audit réalise son travail et renvoie le résultat ; et les changements de statut doivent rester visibles des deux côtés.
Les deux CRM reposent sur deux bases PostgreSQL physiquement séparées — ce qui constitue déjà un premier niveau de cloisonnement, mais interdit toute jointure. L'échange devait donc passer par des appels réseau entre deux systèmes indépendants, avec tout ce que cela implique de contrats d'interface et de gestion d'erreurs.
Les contrats de payload
J'ai conçu, de chaque côté, un ensemble de fonctions serveur d'intégration : des points d'entrée qui reçoivent les dossiers, les résultats d'audit et les mises à jour de statut, et des fonctions sortantes qui notifient l'autre système. Le travail de fond a été de définir les contrats de payload — la forme exacte de ce qui part, de ce qui est accepté, et ce qui se passe en cas de conflit.
Car le plus délicat n'est pas d'envoyer la donnée. C'est de décider ce qui gagne quand les deux systèmes modifient la même chose au même moment. Chaque échange est idempotent par une clé stable : un message rejoué met à jour l'existant au lieu de le dupliquer. Pour les entités enfant — biens, documents, notes — j'ai adopté une logique « insérer si absent », qui préserve les éditions faites côté destinataire.
Le lot qu'on ne pouvait pas rater
Un second lot de dossiers, tertiaire celui-là, présentait un risque maximal : au moment de le traiter, le client n'avait plus accès à l'ancien outil. Les quelque 313 dossiers concernés n'existaient plus que dans la table tampon. Toute erreur signifiait une perte définitive.
J'ai figé et sauvegardé la donnée brute, puis construit un traitement reconnaissant cette structure particulière. Surtout, il a fallu établir une règle métier de répartition en trois groupes, selon que le dossier devait être créé d'un côté, exister des deux, ou n'aller que vers l'autre système.
- Jamais présent côté audit. Pont direct vers le CRM commercial, sans création côté audit.
- Statut réel à refléter des deux côtés. Création côté audit, puis synchronisation vers le commercial.
- Clôturé d'un côté, actif de l'autre. Conservation du statut métier réel côté commercial — plutôt qu'un statut uniforme qui aurait été faux.
Le CRM d'audit servait ici de seule autorité de traduction des statuts, l'autre système ne sachant pas interpréter le format de l'ancien outil. Cette discrimination par groupes a évité l'erreur la plus grossière — faire atterrir des centaines de dossiers dans un statut faux, sur une source irrécupérable. Bilan : 308 dossiers poussés sans un seul échec, 136 créés côté audit, les enregistrements de test correctement exclus, et le résultat vérifié des deux côtés.
La valeur d'une migration ne se mesure pas à sa rapidité mais à la preuve qu'aucune donnée n'a été perdue.
Prouver que les deux bases disent la même chose
Affirmer que deux bases sont « en miroir » ne vaut rien sans preuve. J'ai donc systématisé des contrôles d'intégrité bilatéraux : pour chaque lot, vérifier que chaque dossier d'un côté a son correspondant de l'autre — et l'inverse, afin de détecter aussi bien les manques que les orphelins. Ces grilles (totaux par unité, par statut, absence de doublons) étaient jouées des deux côtés puis comparées. Environ 770 dossiers ont été contrôlés ainsi, sans perte connue, et un lot de 204 dossiers poussé sans un doublon.
Un incident a montré que la qualité des données de référence est aussi critique que celle des dossiers. Des comptes d'utilisateurs avaient été rattachés à la mauvaise unité lors de leur migration ; conséquence, la règle de sécurité — qui compare l'unité de l'utilisateur à celle du dossier — les rendait aveugles à leurs propres dossiers. Le réflexe naturel aurait été d'accuser la règle. En réalité la règle était juste, c'était la donnée qui était fausse. J'ai repointé 38 comptes vers leur véritable unité et corrigé le script fautif.
Cloisonner dans la base, pas dans l'écran
Un CRM multi-acteurs ne doit jamais laisser une agence voir les dossiers d'une autre. J'ai construit ce cloisonnement sur la Row-Level Security de PostgreSQL : la règle d'accès vit dans la base, au plus près de la donnée, et s'applique même à une requête directe — un bug d'interface ne peut pas la contourner.
La difficulté tenait aux rôles transverses : un planificateur, un inspecteur ou un thermicien doivent légitimement voir des dossiers hors de leur propre unité. Je les ai traités par des fonctions d'aide déterminant l'appartenance à un rôle ou l'assignation à un dossier, plutôt que par des exceptions codées en dur — pour garder des politiques lisibles. Au-dessus, une table de permissions par rôle pilote finement les droits, de l'ordre de soixante droits distincts.
L'incident le plus marquant du stage
Une fuite d'isolation a été détectée : pendant un temps, n'importe quel compte pouvait voir les dossiers de toutes les agences. La cause était subtile. Une migration avait recréé une vue enrichie sans réappliquer l'option qui force la vue à s'exécuter avec les droits de l'appelant. Privée de cette option, la vue tournait avec les droits de son propriétaire et court-circuitait toute la sécurité au niveau ligne.
Durcir la politique de la table ne suffisait pas : l'interface lit la vue, pas la table. J'ai corrigé en deux temps — durcissement de la politique, puis réassertion de l'option sur les vues concernées — avant de mener un audit complet de la base. Cet audit a révélé une seconde faille : une table de prospects contenant des données personnelles dont la sécurité au niveau ligne était purement et simplement désactivée. Fermée en urgence.
J'en ai tiré une règle d'or : toute migration touchant une vue enrichie doit impérativement se terminer par la réassertion de cette option. Et un enseignement plus large : en sécurité des données, le maillon faible n'est pas toujours la règle qu'on écrit, mais l'effet de bord d'une opération en apparence anodine.
Livrer sur deux bases vivantes à la fois
- Migrations de schéma en trois temps. Additif d'abord — la nouvelle colonne est nullable, l'ancienne et la nouvelle structure coexistent. Double écriture ensuite, le temps de vérifier sur des données réelles. Nettoyage en dernier, jamais avant. Chaque migration précédée d'une sauvegarde des deux bases, et jouée manuellement après relecture : c'est lent, mais ça force la vigilance.
- Un piège d'authentification serveur-à-serveur. Les fonctions d'intégration s'authentifient par secret partagé, pas par jeton utilisateur. Un redéploiement sans l'option qui désactive la vérification du jeton réactivait une barrière et faisait échouer tous les appels avant même l'exécution du code. Un refus sur un appel par secret signale presque toujours une option de déploiement oubliée, pas un mauvais secret.
- Activation progressive. Jamais de bascule générale d'un coup sur les changements risqués : un utilisateur de test, puis une agence, puis l'ensemble. On détecte le problème sur un périmètre réduit avant qu'il ne touche deux cents personnes.
- Une trentaine de notes de coordination. Chaque changement touchant les deux systèmes faisait l'objet d'une note écrite servant de contrat partagé : quelle unité pré-créer de l'autre côté, quelle fiche autoriser, quel correctif redéployer et quand. Rétrospectivement, une forme légère de gestion de configuration distribuée.
Une limite que j'assume : il n'existait pas d'alerte automatique sur la cohérence des données. Le suivi reposait sur des contrôles manuels après opération et sur le journal d'audit en base. La vigilance humaine restait indispensable.