Sul campo la rete non c’è sempre
Seminterrati, locali tecnici, tetti: un tecnico deve poter registrare gli orari, prendere note e scattare foto senza connessione. L’app mobile, in Flutter, scrive quindi ogni azione in un database locale, in una coda di invio (outbox), e la sincronizza a lotti non appena la rete torna. Il database locale serve a lavorare offline; il server resta la fonte di verità.
Gli eventi sincronizzati coprono l’intero intervento:
- arrivo, inizio, pausa, ripresa e fine del lavoro;
- note e misure strutturate;
- ricambi utilizzati o non disponibili;
- foto e firma del cliente;
- cliente assente, accesso bloccato, pericolo, nuovo intervento necessario, messa in sicurezza.
Un identificativo per evento
Ogni evento riceve sul telefono un clientEventId (UUID v4), che non viene mai rigenerato a un nuovo tentativo. Ripetere esattamente lo stesso evento non ha alcun effetto, anche dopo la chiusura dell’intervento: la risposta lo segnala semplicemente come duplicato. Riutilizzare lo stesso identificativo con un contenuto diverso viene rifiutato (409).
L’app può quindi reinviare un intero lotto dopo aver perso una risposta di rete, senza rischio di duplicati. Un lotto contiene al massimo 100 eventi ed è transazionale: se uno di essi non è valido, non ne viene creato nessuno. Il server controlla anche la sequenza delle registrazioni orarie; una pausa senza inizio o due inizi consecutivi vengono rifiutati. Un ricambio dichiarato scala la giacenza del veicolo una sola volta, anche se l’invio viene ripetuto.
File verificati prima di essere accettati
Prima di inviare una foto o una firma, l’app dichiara un’intenzione di caricamento: tipo MIME, dimensione e impronta SHA-256. Il server risponde con un URL dotato di un token casuale, valido quindici minuti e utilizzabile una sola volta.
Alla ricezione, verifica il token, la dimensione, l’impronta e la firma binaria reale del file. Solo un file convalidato, appartenente alla stessa organizzazione e allo stesso intervento, può poi essere citato da un evento sul campo. I file finiscono in uno storage a oggetti (S3 o compatibile, come MinIO), non in PostgreSQL.
Un flusso in tempo reale che riprende da dove si era fermato
Le schermate di dispatch e l’app ricevono le modifiche tramite un flusso SSE autenticato e isolato per organizzazione. Dopo un’interruzione, il client riprende dall’ultimo evento ricevuto grazie all’intestazione Last-Event-ID; gli eventi sono conservati sette giorni per impostazione predefinita. Un tecnico riceve solo quelli degli interventi a lui assegnati.
Le notifiche push (Firebase Cloud Messaging) restano volutamente generiche: trasportano solo identificativi, e l’app ricarica sempre i dati operativi tramite l’API autenticata.
Cosa cambia l’idempotenza
Mette la complessità nel posto giusto. L’app non ha bisogno di sapere se un invio precedente è andato a buon fine: nel dubbio, reinvia. Il server garantisce che uno stesso fatto venga registrato una sola volta.


