Aller au contenu

Couche de synchronisation runtime du Catalog

Statut

Document d'architecture pour la transmission du projet.

À quoi sert cette couche ?

La Runtime Synchronization Layer, ou couche de synchronisation runtime, organise la propagation des changements du Product Domain et des offres normalisées vers les projections du Catalog.

Elle répond à une question simple :

lorsqu'un produit ou son état amont change, comment faire en sorte que le catalogue finisse lui aussi par refléter ce changement ?

Elle ne décide pas de la vérité métier. Elle transporte et orchestre les changements entre les composants responsables.

Vue simplifiée

Product Domain / offres normalisées
(source amont)
      ↓ changement / finalisation
Couche de synchronisation runtime
      ↓ déclenchement
Moteur de projection Catalog
      ↓ reconstruction / mise à jour
Catalog Domain
(structures de lecture)
      ↓
Frontend

La cohérence éventuelle

Le système suit un modèle de cohérence éventuelle (eventual consistency).

Cela signifie qu'après une modification de l’état amont, le Catalog peut être temporairement en retard. Ce retard n'est acceptable que si le mécanisme de synchronisation permet ensuite au catalogue de converger automatiquement vers l'état attendu et rend les échecs observables.

Exemple :

14:00:00  état amont mis à jour
14:00:01  ancienne projection encore lisible
14:00:03  projection recalculée
14:00:04  catalogue cohérent avec l'amont

La projection ne devient pas source de vérité pendant cette période.

Responsabilités

Cette couche doit notamment :

  • détecter ou recevoir les changements utiles ;
  • capturer les événements ou fins de run utiles à la projection ;
  • déclencher les mises à jour du Catalog ;
  • empêcher qu'un même événement produise plusieurs effets indésirables ;
  • coordonner les reconstructions ;
  • permettre un rejeu lorsque cela est nécessaire ;
  • rendre les échecs observables et récupérables.

Implémentation CURRENT du refresh produit

La finalisation d’un run marchand réussi ne s’arrête pas à l’écriture de ccx_offers_norm_v2.

Le chemin CURRENT est :

upsertNormalizedOffers(feed, run)
        ↓
ccx_sync_refresh_product_projections(feed)
        ↓
ccx_product_canonical_projection_refresh()
        ↓
rebuild Product Models pour les verticales concernées
        ↓
finalisation du run

Le refresh de ccx_canonical_products_v1 est donc une dépendance de correction du sync. S’il échoue, la finalisation signale une erreur critique au lieu de déclarer le run réussi avec une projection silencieusement obsolète.

Le refresh canonical utilise un lock dédié et appelle actuellement un rebuild global de la projection à partir de toutes les lignes ccx_offers_norm_v2 ayant un product_identity_key non vide.

Cette stratégie globale est correcte pour la convergence actuelle mais n’est pas la stratégie de scalabilité cible ; elle est suivie comme dette technique.

Événements d'entrée

Selon l'implémentation, la couche peut réagir à des événements tels que :

  • ProductCreated — création d'un Product ;
  • ProductUpdated — modification d'un Product ;
  • ProductVariantChanged — changement d'une variante ;
  • ProductObservationAdded — nouvelle observation ;
  • ProductConflictCreated — conflit détecté ;
  • ProductConflictResolved — conflit résolu ;
  • finalisation réussie d’un run marchand ;
  • replay explicite d’une offre.

Les noms exacts peuvent évoluer. Ce qui compte architecturalement est la signification du changement et la capacité à le traiter correctement.

Actions de sortie

La couche peut demander des opérations telles que :

  • CatalogProjectionUpdate — mise à jour d'une projection ;
  • CatalogItemRebuild — reconstruction d'un élément catalogue ;
  • CatalogViewRefresh — rafraîchissement d'une vue ;
  • CatalogCollectionUpdate — mise à jour d'une collection ;
  • refresh ou rebuild d’une projection persistée explicitement identifiée.

Ces actions sont des demandes d'orchestration. Elles ne doivent pas contenir la décision métier sur l'identité du Product.

Trois modes à connaître

Mise à jour incrémentale

C'est le mode cible lorsque le périmètre affecté peut être déterminé proprement : seul le périmètre concerné par un changement est recalculé.

Avantage : plus rapide qu'une reconstruction complète.

Condition importante : le résultat final doit converger vers celui qu'aurait produit un rebuild complet avec les mêmes données et règles.

Reconstruction complète

Un full rebuild peut être nécessaire lorsque :

  • le schéma ou la structure de projection change ;
  • une règle majeure de projection évolue ;
  • une dérive de cohérence est détectée ;
  • les données dérivées ne sont plus considérées comme fiables.

Le rebuild repart des sources autoritatives ou amont certifiées et régénère les projections.

ccx_canonical_products_v1 utilise actuellement ce mode complet à chaque refresh canonical.

Rejeu

Le replay consiste à retraiter un événement ou un périmètre déjà connu.

Le replay d’offre CURRENT distingue dry-run et apply. En mode apply, l’offre et ses écritures associées sont commitées, puis la projection canonical est rafraîchie.

Si ce refresh post-commit échoue, l’échec est remonté explicitement. La donnée d’offre déjà commitée ne doit pas être compensée par une modification manuelle de la projection : il faut rejouer le refresh/rebuild jusqu’à convergence.

Idempotence : une règle essentielle

Une opération est idempotente lorsqu'on peut la rejouer sans créer de corruption ou de doublon.

Exemple attendu :

traiter événement X une fois  → état A
traiter événement X deux fois → toujours état A

Pas :

traiter événement X deux fois → deux CatalogItem identiques

Cette propriété est essentielle parce qu'un système réel peut retenter une opération après un timeout ou une panne.

Ordre des événements

Lorsque cela est possible, les événements concernant un même ProductId doivent être traités dans un ordre cohérent.

Sinon, une ancienne modification pourrait arriver après une plus récente et produire une projection obsolète.

L'architecture doit donc prendre en compte :

  • l'ordre ;
  • la déduplication ;
  • les retries ;
  • la version ou la fraîcheur de l'état traité lorsque nécessaire ;
  • les locks de reconstruction lorsque plusieurs chemins peuvent déclencher le même refresh.

Gestion des erreurs

Une panne de synchronisation ne doit pas corrompre la vérité Product ni être masquée par le frontend.

Le comportement attendu est plutôt :

changement amont valide
        ↓
échec de projection
        ↓
erreur observable
        ↓
retry / replay / rebuild
        ↓
health check
        ↓
Catalog à nouveau cohérent

Pour les projections produit CURRENT, ccx health projections fournit notamment le contrôle de couverture missing_canonical_product_identity.

Il faut éviter les mécanismes où l'échec d'une projection entraîne une modification destructive de la source simplement pour faire disparaître l'erreur.

Frontière d'architecture

La couche de synchronisation ne doit pas :

  • modifier les règles du Product Domain ;
  • résoudre l'identité canonique ;
  • inventer des attributs ;
  • interpréter directement une logique spécifique marchand ;
  • devenir elle-même un moteur métier ;
  • considérer une projection comme la source de vérité.

Son rôle est orchestration + propagation + récupération.

Architecture événementielle : attention au vocabulaire

Le système est pensé selon des principes événementiels, même si l'implémentation actuelle est hybride et comprend des finalisations synchrones de run.

Il ne faut donc pas déduire de cette documentation qu'un Kafka ou un bus distribué particulier est obligatoirement déployé aujourd'hui.

Une évolution future pourrait utiliser :

  • une abstraction de bus d'événements ;
  • une file distribuée ;
  • un moteur de traitement de flux.

Mais la technologie choisie reste un détail d'infrastructure tant que le contrat d'architecture est respecté.

Comment diagnostiquer une désynchronisation

Si la source amont est correcte mais que le catalogue montre encore une ancienne valeur ou redirige vers un fallback inattendu :

1. Vérifier l'état amont autoritatif / normalisé
        ↓
2. Vérifier que le changement a été détecté ou finalisé
        ↓
3. Vérifier que la synchronisation a déclenché la projection
        ↓
4. Exécuter le health check de projection
        ↓
5. Vérifier le résultat du moteur de projection
        ↓
6. Vérifier l'écriture de la projection
        ↓
7. Vérifier que le frontend lit la projection attendue

Cette chaîne est beaucoup plus sûre qu'un UPDATE ou INSERT manuel dans une table dérivée.

Pour la reprise du projet

Retenir quatre mots :

propager, rejouer, converger, observer.

La synchronisation transporte les changements ; elle doit pouvoir être rejouée ; le catalogue doit converger vers la vérité ; et les échecs doivent être visibles.

À lire ensuite