As aplicações de campo no nosso mercado têm de assumir que não há rede. Os leitores de contadores percorrem rotas sem cobertura, os técnicos trabalham dentro de edifícios que bloqueiam o sinal, e os agentes deslocam-se entre distritos onde a conectividade vai e vem. Uma aplicação que se degrada com elegância não chega — tem de funcionar completamente offline e depois reconciliar sem perder nada.
Local-first, não cache-first
A distinção é importante. Uma aplicação cache-first trata o servidor como autoritativo e o dispositivo como uma cópia temporária; quando os dois divergem, o dispositivo perde normalmente. Uma aplicação local-first trata a escrita no dispositivo como um evento real, com identidade e data próprias, em fila para sincronização. O utilizador nunca espera por uma ida ao servidor para saber que o seu trabalho ficou guardado.
- Cada escrita é um acrescento a um registo local de operações, não uma alteração no lugar
- Cada operação leva um UUID gerado no cliente, para que as repetições sejam idempotentes
- A interface lê exclusivamente do estado local — o estado da sincronização é um sinal separado e visível
- A sincronização é um processo em segundo plano que pode falhar e repetir sem bloquear o trabalho
As regras de conflito são do negócio, não do código
O erro mais comum é inventar a resolução de conflitos na camada de sincronização. Saber se a alteração de estado de um supervisor deve prevalecer sobre a de um técnico é uma decisão de negócio com consequências operacionais. Levamos essas regras à equipa de operações, escrevemo-las, e implementamos exactamente o que foi acordado.
Na prática, uma pequena taxonomia cobre a maioria dos casos: campos descritivos usam última escrita vence, transições de estado são autoritativas no servidor e validadas contra uma máquina de estados, e quantidades numéricas que representam contagens físicas nunca são fundidas — geram uma excepção para uma pessoa resolver.
type Resolucao = "ultima-escrita-vence" | "servidor-autoritativo" | "revisao-manual";
const politicaDeConflito: Record<string, Resolucao> = {
"trabalho.notas": "ultima-escrita-vence",
"trabalho.estado": "servidor-autoritativo",
"leitura.valor": "revisao-manual",
};Tornar o estado da sincronização visível
Os utilizadores confiam numa aplicação offline quando conseguem ver o que ela está a fazer. Um indicador permanente com as contagens de pendentes, em sincronização e falhados — com forma de inspeccionar as falhas — transforma um processo invisível em segundo plano em algo que um operador consegue compreender e escalar.
Se um trabalhador de campo não consegue saber se o trabalho da manhã chegou ao servidor, vai manter uma cópia em papel. E aí ficamos outra vez com dois sistemas.
