Le terrain n’a pas toujours de réseau

Sous-sol, local technique, toiture : un technicien doit pouvoir pointer, prendre des notes et des photos sans connexion. L’application mobile, en Flutter, écrit donc chaque action dans une base locale, dans une file d’attente (outbox), puis la synchronise par lots dès que le réseau revient. La base locale sert à travailler hors ligne ; le serveur reste la source de vérité.

Les événements synchronisés couvrent toute l’intervention :

  • arrivée, début, pause, reprise et fin de travail ;
  • notes et mesures structurées ;
  • pièces utilisées ou indisponibles ;
  • photos et signature du client ;
  • absence du client, accès bloqué, danger, retour nécessaire, mise en sécurité.

Un identifiant par événement

Chaque événement reçoit sur le téléphone un clientEventId (UUID v4), qui n’est jamais régénéré lors d’une nouvelle tentative. Rejouer exactement le même événement est sans effet, même après la clôture de l’intervention : la réponse le signale simplement comme doublon. Réutiliser le même identifiant avec un autre contenu est refusé (409).

Le mobile peut donc renvoyer un lot entier après une perte de réponse réseau, sans risque de doublon. Un lot compte au maximum 100 événements et il est transactionnel : si l’un d’eux est invalide, aucun n’est créé. Le serveur contrôle aussi la chronologie des pointages ; une pause sans début ou deux débuts consécutifs sont rejetés. Une pièce déclarée ne décrémente le stock du véhicule qu’une seule fois, même si l’envoi est rejoué.

Des médias vérifiés avant d’être acceptés

Avant d’envoyer une photo ou une signature, le mobile déclare une intention d’envoi : type MIME, taille et empreinte SHA-256. Le serveur répond avec une URL munie d’un jeton aléatoire, valable quinze minutes et utilisable une seule fois.

À la réception, il vérifie le jeton, la taille, l’empreinte et la signature binaire réelle du fichier. Seul un média validé, appartenant à la même organisation et à la même intervention, peut ensuite être cité par un événement terrain. Les fichiers sont rangés dans un stockage objet (S3 ou compatible, comme MinIO), pas dans PostgreSQL.

Un flux temps réel qui reprend où il s’était arrêté

Les écrans de dispatch et l’application reçoivent les changements par un flux SSE authentifié et isolé par organisation. Après une coupure, le client reprend au dernier événement reçu grâce à l’en-tête Last-Event-ID ; les événements sont conservés sept jours par défaut. Un technicien ne reçoit que ceux des interventions qui lui sont affectées.

Les notifications push (Firebase Cloud Messaging) restent volontairement génériques : elles ne transportent que des identifiants, et l’application recharge toujours les informations métier via l’API authentifiée.

Ce que l’idempotence change

Elle place la complexité au bon endroit. Le mobile n’a pas besoin de savoir si un envoi précédent a abouti : dans le doute, il renvoie. Le serveur garantit qu’un même fait n’est enregistré qu’une seule fois.