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:
- Continuidade funcional mínima
- Persistência local confiável
- Sincronização eventual consistente
- Tratamento de conflitos
- 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