HomeSharpStack
offline-first5 min

Entendendo Modelos de Dados Offline-First: Projetando seu Schema de Banco de Dados Local

Entendendo Modelos de Dados Offline-First: Projetando seu Schema de Banco de Dados Local

O que é um Schema Offline-First?

Imagine que você está construindo um caderno de anotações. Se você tivesse que conectar à internet toda vez que quisesse escrever algo, seria frustrante, certo? Um schema offline-first é como organizar esse caderno de forma que você possa escrever livremente sem internet, e depois sincronizar suas anotações quando a conexão voltar.

Um schema é simplesmente a estrutura de como seus dados são organizados no banco de dados local. Pense nele como o "plano" de uma casa: define quais cômodos existem, como eles se conectam e o que você pode guardar em cada um.

Princípios Fundamentais

1. Dados Locais Primeiro

Seu aplicativo deve funcionar completamente sem internet. Todos os dados importantes precisam ser armazenados localmente no dispositivo do usuário. Quando a internet volta, você sincroniza com o servidor.

2. Rastreamento de Mudanças

Você precisa saber quais dados foram criados, modificados ou deletados localmente. É como manter um diário de alterações: "Criei esta nota às 14:30", "Editei aquela nota às 15:00".

3. Resolução de Conflitos

E se você editar um documento offline e alguém editar o mesmo documento no servidor? Seu schema precisa ajudar a resolver isso. Você pode manter versões, timestamps ou metadados para decidir qual versão é a "correta".

Estruturando seu Schema

Vamos criar um exemplo simples de um aplicativo de tarefas que funciona offline:

-- Tabela principal de tarefas
CREATE TABLE tasks (
  id TEXT PRIMARY KEY,
  title TEXT NOT NULL,
  description TEXT,
  completed BOOLEAN DEFAULT 0,
  created_at INTEGER,
  updated_at INTEGER,
  synced BOOLEAN DEFAULT 0,
  deleted BOOLEAN DEFAULT 0
);

-- Tabela de histórico de sincronização
CREATE TABLE sync_log (
  id INTEGER PRIMARY KEY,
  task_id TEXT,
  operation TEXT,
  timestamp INTEGER,
  synced BOOLEAN DEFAULT 0
);

Vamos entender cada campo:

  • id: Um identificador único. Use UUIDs para que não haja conflitos quando sincronizar com o servidor.
  • created_at / updated_at: Timestamps que ajudam a resolver conflitos ("qual versão é mais recente?").
  • synced: Um flag que diz se este dado já foi enviado ao servidor.
  • deleted: Em vez de deletar dados imediatamente, marcamos como deletado. Isso permite sincronizar a deleção depois.

Exemplo Prático: Inserindo Dados

Aqui está como você criaria uma nova tarefa localmente (pseudocódigo em C++):

// Criar uma nova tarefa localmente
void createTask(string title, string description) {
  string taskId = generateUUID(); // Gera ID único
  long now = getCurrentTimestamp();
  
  // Inserir no banco local
  database.execute(
    "INSERT INTO tasks (id, title, description, created_at, updated_at, synced) "
    "VALUES (?, ?, ?, ?, ?, 0)",
    taskId, title, description, now, now
  );
  
  // Registrar no log de sincronização
  database.execute(
    "INSERT INTO sync_log (task_id, operation, timestamp, synced) "
    "VALUES (?, 'CREATE', ?, 0)",
    taskId, now
  );
}

Sincronização: O Momento da Verdade

Quando a internet volta, você envia apenas os dados que não foram sincronizados:

// Sincronizar dados não sincronizados
void syncWithServer() {
  // Buscar todas as operações não sincronizadas
  auto unsyncedOps = database.query(
    "SELECT * FROM sync_log WHERE synced = 0"
  );
  
  for (auto op : unsyncedOps) {
    // Enviar para servidor
    sendToServer(op);
    
    // Marcar como sincronizado
    database.execute(
      "UPDATE sync_log SET synced = 1 WHERE id = ?",
      op.id
    );
  }
}

Resolvendo Conflitos

Se o servidor disser "essa tarefa foi modificada por outro usuário", você pode usar timestamps para decidir:

  • Last-Write-Wins: A versão mais recente (maior timestamp) vence.
  • Merge Manual: Mostrar ao usuário ambas as versões e deixar escolher.
  • Versioning: Manter ambas as versões e deixar o usuário decidir depois.

Boas Práticas

  1. Use UUIDs para IDs: Não use IDs auto-incrementados, pois podem colidir quando sincronizar.
  2. Sempre rastreie timestamps: São essenciais para resolver conflitos.
  3. Marque dados como deletados: Não delete imediatamente; marque com um flag.
  4. Mantenha um log de sincronização: Saiba exatamente o que precisa ser sincronizado.
  5. Teste offline: Sempre teste seu aplicativo sem internet para garantir que funciona.

Key Takeaways

  • Um schema offline-first organiza dados localmente com campos especiais (synced, deleted, timestamps) para rastrear mudanças e permitir sincronização posterior com o servidor
  • Use UUIDs para IDs, timestamps para resolver conflitos, e flags de sincronização para saber quais dados ainda precisam ser enviados ao servidor
  • Marque dados como deletados em vez de removê-los imediatamente, permitindo que a deleção seja sincronizada quando a conexão voltar

Enjoyed this reading?

SharpStack delivers personalized tech readings every day, calibrated to your skill level. 5 minutes a day to stay sharp.

“Stay sharp. At your pace. Everyday.”