Aller au contenu

PD-171 — Implémenter la synchronisation multi-device avec résolution de conflits

EPIC de référence : PD-186 — BACKEND CORE


1. Objectif

Définir le contrat canonique d’un mécanisme de synchronisation multi-device permettant à un même utilisateur (ou entité logique) d’accéder et de modifier des données depuis plusieurs dispositifs, avec : - une détection déterministe des conflits, - une résolution explicite et traçable de ces conflits, - une cohérence finale garantie entre tous les dispositifs synchronisés,

tout en respectant des exigences strictes de robustesse, sécurité, prévisibilité et auditabilité.


1.1 Principe fondateur — Interopérabilité sans compromis (Normative)

Le protocole défini par la présente spécification est indépendant des mécanismes de transport, de stockage et d’infrastructure.

L’interopérabilité future du mécanisme de synchronisation NE DOIT PAS être obtenue par affaiblissement des règles, mais exclusivement par adaptation des mécanismes.

À ce titre : - les invariants définis par la spécification sont intangibles ; - toute implémentation ou intégration externe DOIT manipuler un modèle logique canonique conforme à la présente spécification ; - toute adaptation à un autre backend, mode de synchronisation ou infrastructure DOIT être réalisée via des mécanismes d’adaptation explicites, sans modification des règles de versionnement, de résolution de conflits, d’historisation, d’idempotence ou de traçabilité.

Toute implémentation revendiquant la conformité à la présente spécification DOIT satisfaire une suite de tests de conformité couvrant les cas nominaux, les conflits, les périodes offline prolongées et les scénarios de sécurité.


2. Périmètre / Hors périmètre

Périmètre

  • Synchronisation de documents complets et de leurs métadonnées associées entre plusieurs dispositifs associés à une même identité.
  • Synchronisation à l’échelle de l’objet complet (pas de synchronisation par champs/deltas) afin de garantir déterminisme, auditabilité et simplicité.
  • Support de fonctionnement offline / online avec rattrapage ultérieur.
  • Détection des conflits lors de mises à jour concurrentes.
  • Application de règles de résolution de conflits définies contractuellement.
  • Gestion des versions, horodatages logiques et métadonnées de synchronisation.
  • Traçabilité minimale des opérations de synchronisation et de résolution.

Hors périmètre

  • Algorithmes avancés de réplication distribuée (CRDT complets, OT complexes) : hors périmètre.
  • Synchronisation en temps réel bidirectionnelle (push continu) : hors périmètre.
  • UI/UX de résolution manuelle de conflits : hors périmètre.
  • Synchronisation inter-comptes ou inter-utilisateurs : hors périmètre.
  • Sauvegarde/restauration globale : hors périmètre.

3. Définitions

  • Device : instance cliente identifiée de manière unique (mobile, desktop, web).
  • État local : version des données détenue par un device.
  • État de référence : version canonique des données côté backend.
  • Version logique : identifiant de version monotone (ex. numéro ou horodatage logique).
  • Conflit : situation où deux ou plusieurs modifications concurrentes ne peuvent être appliquées automatiquement sans règle explicite.
  • Résolution de conflit : règle déterminant l’état final accepté.
  • Synchronisation : processus d’échange d’états et/ou de deltas entre device et backend.

4. Invariants (non négociables)

  1. Aucune perte silencieuse de données n’est autorisée : toute résolution doit être explicite et traçable.
  2. Chaque modification doit être associée à :
  3. un device émetteur,
  4. une version logique de départ,
  5. une version logique résultante.
  6. La détection de conflit doit être déterministe : mêmes états → même décision.
  7. Les règles de résolution de conflits doivent être connues, documentées et testables.
  8. En cas de conflit non résoluble automatiquement, l’opération doit être rejetée explicitement.
  9. Le backend détient toujours l’état canonique final.
  10. Un device ne peut appliquer une mise à jour que s’il connaît la version de référence attendue.
  11. Les opérations de synchronisation doivent être idempotentes au sens protocolaire (cf. §5.9).

4.1 Modèle de version logique (Normative)

4.1.1 Objectif

Définir un modèle de version logique monotone, non ambigu et testable, utilisé par l’ensemble du protocole de synchronisation.

4.1.2 Type de version

La version logique est un compteur entier strictement croissant (version) associé par objet (object_id).

  • Domaine : entier naturel ≥ 0
  • Monotonie : version(n+1) > version(n)

4.1.3 Autorité et portée

  • Autorité : le backend est l’unique autorité d’attribution des versions.
  • Portée : la version est scopée par objet.
  • Les devices NE DOIVENT PAS incrémenter eux-mêmes la version.

4.1.4 Base version requise

Toute requête de synchronisation DOIT inclure une base_version correspondant à la dernière version connue par le device.

4.1.5 Règles d’évolution

  • Si base_version == version_canonique et la requête est appliquée pour la première fois → version := version + 1
  • Si base_version < version_canonique → conflit de type C2 — Stale Update Conflict
  • Si la requête est rejouée avec le même sync_request_idaucun incrément (cf. §5.9)

4.1.6 Propriétés garanties

Ce modèle garantit : - détection déterministe des divergences, - absence de fork de versions, - compatibilité avec l’idempotence protocolaire, - testabilité directe des invariants liés aux versions.


4.2 Versionnement de schéma et compatibilité des clients (Normative)

4.2.1 Principe

Chaque objet synchronisé DOIT inclure un identifiant explicite de version de schéma (schema_version).

Le versionnement de schéma est indépendant de la version logique (version) et ne s’y substitue pas.

4.2.2 Compatibilité descendante (clients obsolètes)

Un client NE DOIT PAS synchroniser s’il ne supporte pas la schema_version courante des objets manipulés.

Toute tentative dans ce cas DOIT être rejetée explicitement par le backend, avec une erreur indiquant la nécessité de mise à jour du client.

Aucun mode dégradé ou comportement implicite n’est autorisé.

4.2.3 Compatibilité ascendante (clients à jour)

Un client supportant une version de schéma donnée DOIT être capable de : - lire, - synchroniser, - exporter

les objets issus de versions de schéma antérieures.

4.2.4 Écriture sur des objets anciens (migration)

Toute écriture effectuée par un client à jour sur un objet dont la schema_version est inférieure à la version courante DOIT : 1. migrer l’objet vers la version de schéma courante ; 2. incrémenter la version logique associée à l’objet ; 3. conserver la version précédente dans l’historique comme version superseded.

Cette migration DOIT être atomique, traçable et déterministe.

4.2.5 Propriétés garanties

Ce mécanisme garantit : - absence de forks de schéma ; - impossibilité de synchronisation incohérente ; - compatibilité probatoire dans le temps ; - mise à jour forcée et contrôlée des clients.


4.3 Gestion des longues périodes offline (Normative)

4.3.1 Principe

Lorsqu’un device reste hors ligne pendant une période prolongée, les modifications locales accumulées peuvent devenir incompatibles avec l’état canonique courant.

Le système NE DOIT PAS tenter de réconcilier automatiquement des écritures basées sur des versions trop anciennes.

4.3.2 Détection de divergence

La règle générale de détection C2 définie en §4.1.5 s'applique : toute tentative de synchronisation pour laquelle base_version < version_canonique DOIT être considérée comme un conflit de type C2 — Stale Update Conflict.

Note : Une divergence est dite excessive lorsque l'écart version_canonique - base_version > 1, indiquant une période offline prolongée. Ce cas reste un C2 standard mais peut déclencher des mécanismes additionnels côté client (notification de resynchronisation complète, alerte utilisateur).

4.3.3 Rejet déterministe et historisation

Les modifications concernées DOIVENT : - être rejetées explicitement ; - être conservées dans l’historique avec le statut rejected ; - référencer la version canonique courante au moment du rejet.

Aucune modification implicite de l’état canonique n’est autorisée.

4.3.4 Resynchronisation obligatoire

Le device DOIT effectuer une resynchronisation complète afin de récupérer l’état canonique courant avant toute nouvelle tentative d’écriture.

4.3.5 Réapplication contrôlée

Toute réapplication ultérieure de modifications rejetées DOIT être explicite, initiée par le client, et produire une nouvelle version logique, pleinement traçable.

Ce mécanisme garantit un comportement prévisible, auditable et non destructif en cas de longues périodes offline.


4.4 Contraintes de performance et priorisation des synchronisations (Normative)

4.4.1 Principe

Le mécanisme de synchronisation DOIT prioriser la disponibilité rapide de l’état courant et des métadonnées actives.

La synchronisation de l’historique NE DOIT PAS bloquer, retarder ou dégrader la synchronisation de l’état canonique.

4.4.2 Séparation des flux

Les flux suivants DOIVENT être logiquement séparés : 1. Synchronisation primaire : état canonique, métadonnées courantes, versions actives. 2. Synchronisation secondaire : historique des versions (superseded, rejected).

La synchronisation secondaire DOIT être effectuée en tâche de fond.

4.4.3 Garanties de non-blocage

  • L’indisponibilité temporaire de l’historique NE DOIT PAS empêcher une synchronisation primaire réussie.
  • Toute opération d’export ou de consultation de l’historique NE DOIT PAS impacter les performances de la synchronisation primaire.

4.4.4 Consistance

La séparation des flux NE DOIT PAS compromettre : - la cohérence de l’état canonique ; - l’exhaustivité de l’historique à terme.

Toute synchronisation secondaire incomplète DOIT pouvoir être reprise sans effet de bord.

4.4.5 Propriétés garanties

Ce modèle garantit : - latence maîtrisée pour les opérations courantes ; - absence de blocage lié à l’historique ; - compatibilité avec une rétention illimitée ; - montée en charge progressive sans dégradation fonctionnelle.


4.5 Intégration sécurité (Normative)

4.5.1 Chiffrement applicatif

Les données synchronisées DOIVENT être considérées comme chiffrées côté client.

Le backend traite les contenus synchronisés comme des blobs opaques et NE DOIT PAS dépendre du contenu en clair pour : - la détection de conflits ; - la résolution de conflits ; - l’historisation des versions.

4.5.2 Isolation des devices

Toute opération de synchronisation DOIT être associée à un identifiant de device (device_id) unique et stable.

Les opérations provenant de devices distincts NE DOIVENT PAS être confondues, rejouées ou attribuées à un autre device.

4.5.3 Rotation d’identifiants et de clés

Toute rotation de clé cryptographique ou d’identité applicative DOIT entraîner la création d’un nouveau device_id.

Il n’existe aucune continuité implicite entre deux devices distincts, même s’ils sont associés à la même identité utilisateur.

4.5.4 Continuité probatoire

Les décisions historiques associées à un device_id ancien NE DOIVENT PAS être modifiées, réattribuées ou supprimées lors d’une rotation.

Le nouveau device DOIT effectuer une synchronisation complète depuis l’état canonique courant.

4.5.5 Propriétés garanties

Ce modèle garantit : - l’isolation stricte des devices ; - la compatibilité avec un chiffrement applicatif de bout en bout ; - la continuité probatoire de l’historique ; - l’absence d’ambiguïté lors des rotations d’identifiants.


5. Politique de résolution des conflits (Normative)

5.1 Objectif

Cette section définit de manière normative et exhaustive les règles de résolution des conflits afin de garantir la déterminisme, la testabilité et l’auditabilité du mécanisme de synchronisation.

5.2 Définitions

  • Objet synchronisé : entité identifiée par un object_id unique.
  • Version logique : valeur monotone associée à un objet.
  • Base version : version logique connue par le device émetteur au moment de la modification.
  • Conflit : impossibilité d’appliquer une mise à jour sans règle explicite.

5.3 Types de conflits couverts (liste exhaustive)

Le système reconnaît exclusivement les types de conflits suivants : - C1 — Concurrent Update Conflict : deux mises à jour concurrentes sur le même objet et la même base version. - C2 — Stale Update Conflict : mise à jour basée sur une version inférieure à la version canonique courante. - C3 — Delete / Update Conflict : mise à jour reçue sur un objet marqué comme supprimé. - C4 — Duplicate Operation Conflict : opération strictement identique déjà appliquée.

Tout conflit ne correspondant pas à l’un de ces cas DOIT être traité comme non résoluble (cf. §5.6).

5.4 Règles déterministes de résolution

  • R1 — C1 (Concurrent Update Conflict) :
  • La règle de résolution par défaut est Last-Write-Wins (LWW).
  • La version possédant la version logique la plus élevée est retenue comme état canonique.
  • La ou les versions perdantes NE DOIVENT PAS être supprimées : elles DOIVENT être conservées dans l’historique de l’objet.

  • R2 — C2 (Stale Update Conflict) :

  • La mise à jour est rejetée.

  • R3 — C3 (Delete / Update Conflict) :

  • La suppression prévaut toujours.
  • Toute mise à jour reçue après suppression est rejetée, mais DOIT être conservée dans l’historique comme tentative invalide.

  • R4 — C4 (Duplicate Operation Conflict) :

  • L’opération est strictement idempotente.
  • Aucun nouvel état ni incrément de version n’est produit.

Note normative — Distinction entre C1 et C2

Le conflit C1 — Concurrent Update Conflict s’applique lorsque plusieurs requêtes de synchronisation portant sur un même objet référencent la même base_version et sont évaluées avant l’engagement canonique de l’une d’elles.

Dans ce cas, ces requêtes sont considérées comme concurrentes, même si leur traitement interne est séquentiel, et DOIVENT être arbitrées selon la règle R1 (LWW).

Le conflit C2 — Stale Update Conflict s’applique exclusivement aux requêtes évaluées après qu’une version canonique a été engagée et dont la base_version est strictement inférieure à la version canonique courante.

Cette distinction garantit la cohérence entre : - l'existence d'un backend autorité unique ; - la possibilité de conflits concurrentiels réels et testables ; - l'applicabilité effective de la règle de résolution LWW.


5.4.1 Fenêtre d'évaluation concurrente (Normative)

Objectif

Définir de manière déterministe et indépendante de l'implémentation la frontière entre les conflits de type C1 (concurrent) et C2 (stale).

Principe

Toute implémentation conforme DOIT définir explicitement une fenêtre d'évaluation concurrente (concurrent_evaluation_window) pendant laquelle les requêtes reçues pour un même objet sont considérées comme potentiellement concurrentes.

Règles de classification

  1. Requêtes dans la même fenêtre :
  2. Toutes les requêtes portant sur le même object_id et la même base_version, reçues dans la même fenêtre d'évaluation, DOIVENT être classifiées comme C1 — Concurrent Update Conflict.
  3. La résolution DOIT appliquer la règle R1 (LWW).

  4. Requêtes hors fenêtre :

  5. Toute requête arrivant après l'engagement canonique d'une version issue de la fenêtre précédente, et dont la base_version < version_canonique, DOIT être classifiée comme C2 — Stale Update Conflict.
  6. La résolution DOIT appliquer la règle R2 (REJECT).

Implémentations conformes

La fenêtre d'évaluation concurrente PEUT être implémentée selon l'une des stratégies suivantes :

Stratégie Description Caractéristiques
Batch temporel Regroupement des requêtes reçues dans un intervalle de temps configurable (ex. 50-100ms) Simple, adapté aux charges modérées
Batch transactionnel Regroupement des requêtes présentes dans la même transaction de lecture Déterministe, isolation forte
Batch explicite Requêtes soumises dans un lot explicite par le client ou le système Contrôle total, complexité accrue

Contraintes normatives

  1. Durée maximale : La fenêtre d'évaluation NE DOIT PAS excéder 1 seconde afin de garantir une latence acceptable.

  2. Configuration explicite : La stratégie et les paramètres de la fenêtre DOIVENT être documentés et configurables.

  3. Déterminisme : À requêtes identiques et configuration identique, la classification DOIT être reproductible.

  4. Traçabilité : L'identifiant de la fenêtre d'évaluation (ex. batch_id) PEUT être inclus dans les métadonnées de traçabilité pour faciliter l'audit.

Propriétés garanties

Cette définition garantit :

  • la délimitation contractuelle de la frontière C1/C2 ;
  • la reproductibilité de la classification des conflits ;
  • la flexibilité d'implémentation sans violation des invariants ;
  • l'absence de dépendance implicite à un état transient non défini.

5.5 Ordre de priorité

Lorsqu’une règle de résolution est applicable, l’ordre de priorité suivant DOIT être respecté : 1. Cohérence du modèle de données 2. Version logique la plus élevée 3. Décision du backend (autorité finale)

Les horodatages NE DOIVENT PAS être utilisés comme critère principal de résolution.

5.6 Cas explicitement non résolubles

Un conflit est considéré comme non résoluble dans les cas suivants (liste exhaustive) : - absence ou incohérence de base_version ; - divergence de version supérieure à 1 sans historique intermédiaire ; - conflit ne correspondant à aucun type défini en §5.3 ; - violation d’un invariant de sécurité ou d’intégrité.

Dans ces cas : - l’opération DOIT être rejetée explicitement ; - aucun état partiel NE DOIT être appliqué.

5.7 Traçabilité obligatoire

Toute résolution ou rejet DOIT produire un événement de traçabilité contenant au minimum : - conflict_id - object_id - conflict_type - rule_applied - input_versions - result (resolved / rejected) - resulting_version (le cas échéant) - timestamp

Ces événements DOIVENT être persistés conformément aux exigences de traçabilité du système.


5.8 Historique des versions et garanties d’accès (Normative)

5.8.1 Principe

Toute version non canonique résultant d’une résolution de conflit (versions perdantes, tentatives rejetées) DOIT être conservée dans un historique des versions associé à l’objet.

5.8.2 Immuabilité

L’historique des versions est strictement en lecture seule : - aucune version historique NE DOIT être modifiée, supprimée ou réordonnée ; - aucune action utilisateur ou système NE PEUT altérer l’historique.

Cette propriété est indépendante de l’état canonique courant.

5.8.3 Volumétrie et rétention

La conservation de l’historique est illimitée.

Le nombre de versions historiques par objet n’est pas borné par le protocole. Cette décision repose sur le fait que l’historique est constitué principalement de métadonnées, dont l’impact volumétrique est jugé acceptable.

Toute optimisation de stockage (compression, archivage froid, indexation) NE DOIT PAS altérer l’exhaustivité ni l’ordre logique de l’historique.

5.8.4 Statut des entrées d’historique

Chaque entrée d’historique DOIT être marquée avec un statut explicite parmi : - canonical : version active - superseded : version remplacée par application de la règle LWW - rejected : tentative rejetée (conflit non résoluble, suppression prioritaire, etc.)

5.8.5 Consultation

  • L’historique DOIT être consultable en lecture seule par des mécanismes dédiés.
  • Aucune opération de synchronisation NE DOIT utiliser l’historique comme base de calcul d’un nouvel état.

5.8.6 Export et audit

  • L’historique DOIT pouvoir être exporté à des fins d’audit.
  • Tout export DOIT inclure les métadonnées de traçabilité associées (versions, règles appliquées, horodatages).
  • L’export NE DOIT PAS permettre la réinjection ou la modification de l’état canonique.

5.9 Propriété clé

La conservation en lecture seule de l’historique garantit : - l’absence de perte silencieuse, - la traçabilité complète des décisions de résolution, - l’impossibilité de reconstitution ambiguë de l’état canonique.

5.8 Propriété clé

À règles identiques, toute implémentation conforme DOIT produire le même résultat pour un même ensemble d’entrées.

5.9 Idempotence protocolaire (Normative)

5.9.1 Identifiant de requête

Toute requête de synchronisation DOIT inclure un identifiant unique sync_request_id. Cet identifiant DOIT être stable en cas de retry réseau et unique à l’échelle du device émetteur.

5.9.2 Règle d’idempotence

Si une requête avec le même sync_request_id a déjà été traitée : - l’état résultant DOIT être retourné tel quel ; - la version logique NE DOIT PAS être incrémentée ; - aucun effet de bord supplémentaire NE DOIT être produit.

5.9.3 Portée

L’idempotence s’applique indépendamment du statut de la requête initiale (succès, résolution de conflit, rejet).

5.9.4 Traçabilité

Le traitement idempotent DOIT être traçable via un événement indiquant le sync_request_id et la réutilisation d’un résultat existant.


5.10 Format canonique d’export d’audit (Normative)

5.10.1 Objectif

Définir le format canonique, déterministe et auto-suffisant d’export des faits issus du mécanisme de synchronisation, permettant une exploitation probatoire sans ambiguïté.

Ce format décrit les décisions prises par la synchronisation, indépendamment de tout mécanisme externe de scellement, signature ou ancrage.

5.10.2 Portée

L’export d’audit couvre exclusivement : - l’objet synchronisé (object_id) ; - l’historique complet des versions (canonical, superseded, rejected) ; - les règles de résolution appliquées ; - les métadonnées nécessaires à l’audit.

5.10.3 Structure générale

L’export DOIT être produit sous la forme d’un document JSON unique, encodé en UTF-8, respectant un ordre de champs déterministe.

Structure logique minimale :

{
  "object_id": "string",
  "exported_at": "RFC3339 timestamp",
  "canonical_version": "integer",
  "history": [
    {
      "version": "integer",
      "base_version": "integer",
      "status": "canonical | superseded | rejected",
      "conflict_type": "C1 | C2 | C3 | C4 | NONE",
      "rule_applied": "R1_LWW | R2_REJECT | R3_DELETE_PREVAILS | R4_IDEMPOTENT | NONE",
      "sync_request_id": "string",
      "device_id": "string",
      "timestamp": "RFC3339 timestamp"
    }
  ]
}
```json
{
  "object_id": "string",
  "exported_at": "RFC3339 timestamp",
  "canonical_version": "integer",
  "history": [
    {
      "version": "integer",
      "base_version": "integer",
      "status": "canonical | superseded | rejected",
      "rule_applied": "LWW | REJECT",
      "sync_request_id": "string",
      "device_id": "string",
      "timestamp": "RFC3339 timestamp"
    }
  ]
}

5.10.4 Contraintes normatives

  • L’ordre des entrées dans history DOIT être strictement croissant par version.
  • Chaque entrée DOIT correspondre à une décision effective de la synchronisation.
  • Aucune information implicite ou dérivable NE DOIT être omise.
  • Le document NE DOIT PAS contenir de signature, de hash ou de scellement externe.

5.10.5 Compatibilité probatoire

Le format est conçu pour : - être scellable ultérieurement (signature, horodatage, ancrage) sans transformation ; - être stocké dans un système WORM ou append-only ; - être lisible et interprétable sans dépendance logicielle propriétaire.

Toute évolution du format DOIT être rétrocompatible ou versionnée explicitement.

À règles identiques, toute implémentation conforme DOIT produire le même résultat pour un même ensemble d’entrées.


6. Flux nominaux

Note normative — Idempotence et incrément de version

L’incrément de version NE DOIT avoir lieu que lors du premier traitement effectif d’une requête de synchronisation donnée. Toute rejoue d’une requête déjà traitée NE DOIT PAS produire de nouvel état ni incrémenter la version logique (cf. §5.9).

Flux N1 — Synchronisation sans conflit

  1. Un device envoie ses modifications avec la version logique connue.
  2. Le backend compare avec l’état de référence.
  3. Les modifications sont compatibles.
  4. Le backend applique les changements et incrémente la version.
  5. Le backend retourne le nouvel état canonique.

Flux N2 — Synchronisation avec conflit détecté

  1. Un device envoie des modifications basées sur une version obsolète.
  2. Le backend détecte une divergence.
  3. Les règles de résolution sont évaluées.
  4. Si une règle s’applique, l’état final est produit et enregistré.
  5. Le backend retourne l’état résolu et les métadonnées de conflit.

Flux N3 — Synchronisation rejetée

  1. Un device envoie une mise à jour incompatible.
  2. Aucune règle automatique ne permet la résolution.
  3. La synchronisation est rejetée explicitement.

6.1 Diagrammes

6.1.1 Diagramme d’états — Cycle de vie d’une version (§5.8.4)

Chaque entrée d’historique associée à un objet synchronisé transite entre trois statuts définis en §5.8.4. L’historique est immuable (INV-1, §5.8.2) : aucune transition inverse n’est autorisée.

stateDiagram-v2
    [*] --> canonical : Première écriture acceptée\n(base_version == version_canonique)

    canonical --> superseded : Résolution LWW (R1)\nUne version concurrente\nest retenue — INV-1, §5.8.2
    canonical --> rejected : Suppression prévaut (R3)\nObjet marqué supprimé — §5.4

    [*] --> rejected : Conflit non résoluble (§5.6)\nou Stale Update C2 (R2)\nou Delete/Update C3 (R3)
    [*] --> superseded : Concurrent Update C1\nversion perdante (R1 — LWW)

    superseded --> [*] : Immuable — lecture seule\n(§5.8.2, INV-1)
    rejected --> [*] : Immuable — lecture seule\n(§5.8.2, INV-1)

6.1.2 Diagramme de séquence — Flux N1 : synchronisation sans conflit (§6)

Illustre le flux nominal N1 avec vérification de version logique (§4.1), idempotence protocolaire (§5.9, INV-8) et traçabilité (§5.7, INV-1).

sequenceDiagram
    participant D as Device
    participant B as Backend
    participant H as Historique (§5.8)

    D->>B: PUT /sync {object_id, base_version, sync_request_id, payload}
    activate B

    B->>B: Vérifier sync_request_id (§5.9 — INV-8)
    alt Requête déjà traitée (C4 — R4)
        B-->>D: 200 OK {état existant, version inchangée}
    else Première soumission
        B->>B: Comparer base_version vs version_canonique (§4.1.5)
        alt base_version == version_canonique
            B->>B: Appliquer modifications
            B->>B: version := version + 1 (§4.1.5)
            B->>H: Persister entrée {status: canonical} (§5.8.4)
            B->>H: Événement traçabilité {conflict_type: NONE} (§5.7 — INV-1)
            B-->>D: 200 OK {nouvel état canonique, nouvelle version}
        else base_version < version_canonique
            B->>H: Persister entrée {status: rejected} (§5.8.4)
            B->>H: Événement traçabilité {conflict_type: C2, rule: R2_REJECT} (§5.7)
            B-->>D: 409 Conflict {version_canonique, motif: C2}
        end
    end

    deactivate B

6.1.3 Diagramme de séquence — Flux N2 : conflit concurrent C1 avec résolution LWW (§5.4)

Illustre deux devices envoyant des modifications concurrentes sur la même base_version dans la fenêtre d’évaluation (§5.4.1). La résolution LWW (R1) retient la version logique la plus élevée (INV-3, INV-4). La version perdante est conservée comme superseded (INV-1).

sequenceDiagram
    participant DA as Device A
    participant DB as Device B
    participant BK as Backend
    participant HI as Historique (§5.8)

    Note over DA, DB: Les deux devices connaissent base_version = V1

    DA->>BK: PUT /sync {object_id, base_version: V1, sync_request_id: A1, payload_A}
    DB->>BK: PUT /sync {object_id, base_version: V1, sync_request_id: B1, payload_B}

    activate BK
    Note over BK: Fenêtre d’évaluation concurrente (§5.4.1)
    BK->>BK: Classifier comme C1 — Concurrent Update (§5.3)
    BK->>BK: Appliquer R1 — LWW : version la plus élevée gagne (§5.4)

    alt payload_B retenu (version V2)
        BK->>HI: {version: V2, status: canonical, rule: R1_LWW} (INV-1, §5.7)
        BK->>HI: {payload_A, status: superseded, rule: R1_LWW} (INV-1)
        BK-->>DB: 200 OK {version: V2, canonical}
        BK-->>DA: 409 Conflict {conflict_type: C1, winning_version: V2}
    end
    deactivate BK

    DA->>BK: GET /sync {object_id}
    BK-->>DA: 200 OK {version: V2, état canonique — resynchronisation §4.3.4}

7. Cas d’erreur

E1. Version inconnue ou absente : la requête ne fournit pas de version logique. - Effet : synchronisation refusée.

E2. Device non reconnu : identifiant de device invalide. - Effet : synchronisation refusée.

E3. Conflit non résoluble : aucune règle applicable. - Effet : rejet explicite avec métadonnées de conflit.

E4. Violation d’invariant : tentative d’écriture sans base cohérente. - Effet : rejet.

E5. Erreur de cohérence interne : état canonique indisponible. - Effet : échec explicite.


8. Critères d’acceptation (testables)

CA1. Deux devices modifiant des données distinctes peuvent synchroniser sans conflit. CA2. Deux devices modifiant la même donnée depuis la même version déclenchent un conflit détecté. CA3. Une règle de résolution documentée est appliquée correctement. CA4. Une mise à jour non résoluble est rejetée explicitement. CA5. L’état canonique final est identique pour tous les devices après synchronisation. CA6. Une synchronisation répétée avec les mêmes paramètres est idempotente.


9. Scénarios de test (Given / When / Then)

S1 — Synchronisation nominale

Given deux devices synchronisés à la version V1 When un device modifie une donnée et synchronise Then la version passe à V2 sans conflit.

S2 — Conflit simple

Given deux devices à la version V1 When chacun modifie la même donnée puis synchronise Then un conflit est détecté et une règle est appliquée.

S3 — Conflit non résoluble

Given deux devices modifiant la même donnée sans règle applicable When la synchronisation est tentée Then la synchronisation est rejetée explicitement.

S4 — Idempotence

Given une synchronisation réussie When la même requête est rejouée Then l’état final reste inchangé.


10. Hypothèses explicites

H1. Les devices sont authentifiés et associés à une identité unique. H2. Une version logique monotone est disponible côté backend. H3. Les règles de résolution de conflits sont définies au niveau backend.


11. Points à clarifier

Aucun.

Tous les principes structurants, règles fonctionnelles, contraintes de sécurité, de performance, de compatibilité et d’interopérabilité sont désormais définis normativement dans la présente spécification.

Toute évolution future DEVRA faire l’objet d’une nouvelle version de la spécification, sans remise en cause rétroactive des invariants établis.