# Moteur de Calcul Unifié pour Next.js et Flutter : Comment Éviter les Divergences dans les Chiffres
Quand le même calculateur de construction existe à la fois sous forme d'application mobile Flutter et de site web Next.js, même de légères différences dans les formules peuvent miner la confiance des utilisateurs. Un cas concret l'a montré : si l'app et le site affichent des volumes de béton différents pour un poteau de clôture, ce n'est pas qu'un bug — c'est un problème systémique de logique dupliquée. La solution : une source unique de vérité basée sur des spécifications JSON déclaratives et des tests de parité.
Problème de Deux Bases de Code Indépendantes
Initialement, la logique de calcul était implémentée deux fois : en Dart pour l'app Flutter et en TypeScript pour le site Next.js. Les deux projets évoluaient en parallèle mais indépendamment. Les coefficients, valeurs par défaut (comme le diamètre du trou pour un poteau), règles d'arrondi et emballages des matériaux vivaient séparément dans le code de chaque runtime. Résultat : des écarts dans les sorties pour les mêmes entrées.
Cette architecture viole le principe DRY (« Ne pas se répéter ») et crée une dette technique qui finit par refaire surface sous forme de plaintes d'utilisateurs. C'est particulièrement critique pour les outils de niche où la précision des calculs impacte directement la confiance dans le produit.
Approche Déclarative : JSON comme Source de Vérité
Au lieu de dupliquer la logique métier, tous les paramètres variables ont été déplacés dans des fichiers JSON structurés — un par calculateur. Chaque spécification contient :
- Valeurs par défaut pour les champs d'entrée (ex. : diamètre du pieu — 150 mm)
- Règles d'arrondi (toujours vers le haut)
- Coefficients de perte selon le mode de précision
- Infos d'emballage des matériaux (sacs de 25 kg, rendement de 0,01 m³ par sac)
- Épaisseurs minimales de couches et espacements des fixations
Ces fichiers JSON sont devenus la source unique de vérité. Désormais, tout changement de paramètre se fait une seule fois — dans le JSON — et s'applique automatiquement dans les deux runtimes.
Génération de Code pour la Synchronisation
Pour garantir que les deux projets utilisent les mêmes données, un processus de génération de code a été introduit :
- Lors des modifications des specs JSON, le script
npm run sync:specss'exécute - Le script lit toutes les specs et génère un fichier Dart avec une map constante (
const Map) - Le fichier généré est commité dans le dépôt de l'app Flutter
- Le CI/CD des deux systèmes vérifie les changements et déploie les versions mises à jour
Le fichier généré est marqué @generated, et les modifications manuelles sont bloquées par le linter. Cela élimine les erreurs humaines et assure la cohérence des données.
Tests de Parité : Protection contre les Divergences Logiques
Même avec des données identiques, les différences entre langages peuvent causer des variances de comportement : Math.ceil() en TypeScript et num.ceil() en Dart peuvent diverger sur des cas limites. Pour l'éviter, un système de tests de parité a été mis en place :
- Un ensemble de fixtures — des dizaines de combinaisons d'entrées pour chacun des 61 calculateurs
- Le moteur TypeScript traite toutes les fixtures et enregistre les résultats dans un fichier de référence
- Le même fichier est utilisé dans le dépôt Flutter pour comparer avec les résultats du moteur Dart
- Toute divergence — même à la dernière décimale — fait échouer le CI
Au cours de six mois, ces tests ont détecté trois divergences réelles qui seraient passées inaperçues sans vérifications automatisées.
Trois Modes de Précision au Lieu de « Plus 10 % »
Les calculateurs traditionnels recourent souvent à un facteur de sécurité universel (ex. : +10 %). Ce raccourci génère des imprécisions : les carreaux nécessitent une marge de découpe, le plâtre une compensation pour les irrégularités du support, et l'adhésif pour l'absorption de surface.
Au lieu de cela, trois modes de calcul ont été implémentés :
- Basique — calcul basé sur les normes sans marges, selon GOST/SP
- Réaliste (par défaut) — prend en compte les conditions typiques de rénovation d'appartement
- Professionnel — marges maximales pour projets complexes et garanties contre les ruptures de stock
Les coefficients de chaque mode sont stockés dans les specs JSON, permettant des ajustements sans recontruction des apps.
Règle clé : pour des entrées identiques, les résultats doivent respecter Basique ≤ Réaliste ≤ Professionnel. Un test CI dédié vérifie cela pour tous les calculateurs.
Validateurs comme Deuxième Ligne de Défense
Au-delà des tests unitaires et de parité, le CI exécute trois validateurs spécialisés :
- Validateur logique : vérifie que la quantité d'achat ≥ quantité nette, que les résultats ne s'annulent pas sur des entrées valides, que les libellés utilisent les bonnes unités métriques (« m² », pas « sq m »)
- Validateur d'emballage : confirme des emballages réalistes (adhésif en seaux de 5/10/15 L, carreaux en packs, pas à l'unité)
- Validateur d'unités : détecte les confusions de système métrique (ex. : additionner millimètres et mètres sans conversion)
Ces scripts jouent le rôle d'un « poste de contrôle de sécurité » à l'entrée du système — ils ne remplacent pas les tests unitaires mais préviennent les erreurs bêtes mais critiques au niveau de l'écosystème.
Enseignements Clés
- Source unique de vérité via les specs JSON élimine la duplication des paramètres entre runtimes.
- Génération de code assure la synchronisation des données sans intervention manuelle.
- Tests de parité garantissent des résultats identiques en Dart et TypeScript.
- Trois modes de précision remplacent le simpliste « +10 % » et donnent aux utilisateurs le contrôle des marges.
- Validateurs spécialisés dans le CI protègent contre les erreurs systémiques que les tests unitaires pourraient rater.
— Editorial Team
Aucun commentaire pour le moment.