Skip to main content

Offline Support | Arquitetura

  • Offline Support | Arquitetura

Offline Support Implementing

Offline support é a capacidade de uma aplicação continuar funcionando, total ou parcialmente, mesmo sem conectividade de rede, mantendo dados localmente e sincronizando quando a conexão é restabelecida.

Esse padrão é comum em aplicações móveis, sistemas de campo, aplicações web progressivas (PWA) e qualquer ambiente com conectividade instável.


Objetivos Arquiteturais

Uma implementação adequada precisa garantir:

  1. Continuidade funcional mínima
  2. Persistência local confiável
  3. Sincronização eventual consistente
  4. Tratamento de conflitos
  5. Visibilidade clara do estado da conexão

Offline support não é apenas cache. Envolve estratégia de sincronização e consistência distribuída.


Modelos de Funcionamento

1. Cache de Leitura (Read Cache)

Permite que dados previamente carregados sejam acessados offline.

Fluxo:

Aplicação → Storage Local → (se online) API

Se offline, a aplicação retorna dados armazenados localmente.

Esse modelo é adequado para dados predominantemente de leitura.


2. Escrita Offline (Outbox Pattern)

As operações de escrita são armazenadas localmente e enviadas ao servidor quando a conexão retorna.

Fluxo:

Usuário → Armazena operação localmente → Fila de sincronização → API

Esse padrão é fundamental para garantir integridade em cenários offline-first.


3. Sync Bidirecional

• Push: envia alterações locais • Pull: atualiza dados locais com alterações remotas

Esse modelo exige tratamento de conflitos.


Componentes Técnicos por Plataforma

Web

  • Service Worker
  • Cache API
  • IndexedDB
  • Background Sync API

Mobile

  • SQLite
  • Realm
  • Room (Android)
  • Core Data (iOS)

Exemplo Web — Service Worker

Registro:

if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}

Cache de recursos:

self.addEventListener('install', event => {
event.waitUntil(
caches.open('app-cache').then(cache => {
return cache.addAll([
'/',
'/index.html',
'/styles.css'
]);
})
);
});

Estratégia Cache First:

self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});

Persistência de Dados com IndexedDB

import { openDB } from 'idb';

const db = await openDB('app-db', 1, {
upgrade(db) {
db.createObjectStore('tasks', { keyPath: 'id' });
},
});

await db.put('tasks', { id: 1, name: 'Tarefa offline' });

Implementação do Outbox Pattern

Salvar operação localmente:

await db.add('outbox', {
id: crypto.randomUUID(),
type: 'CREATE_ORDER',
payload: orderData,
createdAt: Date.now()
});

Sincronização:

async function sync() {
const tx = db.transaction('outbox', 'readwrite');
const store = tx.objectStore('outbox');
const all = await store.getAll();

for (const item of all) {
try {
await fetch('/api/orders', {
method: 'POST',
body: JSON.stringify(item.payload)
});
await store.delete(item.id);
} catch {
// permanece na fila
}
}
}

Tratamento de Conflitos

Esse é o ponto mais crítico.

Modelos comuns:

Last Write Wins (LWW)

A última atualização prevalece. Simples, porém pode causar perda de dados.


Versionamento (Optimistic Concurrency)

Cada registro possui versão:

{
"id": 10,
"version": 3
}

Se o servidor detectar divergência, rejeita a atualização.


Merge Manual

O usuário resolve conflitos manualmente.


CRDT (Conflict-free Replicated Data Types)

Modelo matemático que garante convergência automática sem coordenação central. Usado em sistemas colaborativos.


Estado de Conectividade

Aplicação deve monitorar:

window.addEventListener('online', () => sync());
window.addEventListener('offline', () => console.log('offline'));

A interface deve indicar:

  • Online
  • Offline
  • Sincronizando
  • Erro de sincronização

Boas Práticas

  • Garantir idempotência nas APIs
  • Versionar dados
  • Isolar camada de sincronização
  • Monitorar falhas de sync
  • Testar cenários de reconexão
  • Criptografar dados sensíveis localmente

Riscos e Complexidade

  • Aumento significativo de complexidade
  • Possível divergência temporária
  • Necessidade de resolução de conflitos
  • Armazenamento local limitado

Quando Implementar

  • Aplicações móveis
  • Uso em campo
  • Ambientes com baixa conectividade
  • Sistemas que priorizam UX resiliente

Quando Evitar

  • Sistemas altamente transacionais
  • Operações financeiras críticas
  • Cenários que exigem consistência forte imediata