ArquiteturaIntermediário6 min de leitura6 seçõesanos 1980, Data aproximada

Append-only

Uma estrutura onde a única escrita permitida é acrescentar no fim: nada se edita, nada se apaga. Corrigir é gravar um registro novo que supera o anterior — e é assim que funcionam o WAL do seu banco, o Kafka, o git e o seu extrato.

Em 10 segundos

Só se escreve no fim: nada é editado, nada é apagado. Correção é um novo registro que supera o anterior, o estado atual é uma projeção derivada, e o log vira ao mesmo tempo história, prova e mecanismo de sincronização.

Antes de ter nome

Antes do software, o diário contábil já era append-only por lei: um lançamento errado se corrige com outro lançamento, nunca apagando o anterior.

  • #log
  • #imutabilidade
  • #auditoria
  • #wal
  • #event-log
Analogia

O livro de registros do cartório

Em cartório, ninguém apaga um assento errado — faz-se uma averbação: um registro novo, datado e assinado, dizendo 'o anterior fica retificado assim'. O erro continua visível, a correção também, e qualquer um pode reconstituir a história folheando em ordem. Rasura em cartório não corrige: incrimina.

01Conceito

O problema: o UPDATE é um destruidor de evidências

Toda vez que uma linha é sobrescrita, uma versão do mundo deixa de existir. No dia em que o suporte pergunta 'o endereço de entrega mudou antes ou depois do despacho?', a resposta já foi destruída pela própria operação que causou a dúvida.

E o custo não é só forense: sistemas que precisam reagir a mudanças (réplicas, caches, buscas, times vizinhos) não têm o que consumir — sobra CDC ou gambiarras de polling comparando snapshots.

Você já depende de append-only mesmo sem saber: todo banco relacional grava primeiro num WAL (write-ahead log) e só depois aplica nas páginas — é o que torna crash recovery e replicação possíveis. Kafka é um log distribuído com retenção. O git nunca edita um commit: 'amend' cria outro objeto e move o ponteiro. A ideia sustenta a infraestrutura inteira; o padrão é trazê-la para o seu modelo de dados quando a história importa.

A diferença para Event Sourcing: append-only é a propriedade da estrutura (só anexa); Event Sourcing é uma decisão de arquitetura que usa essa estrutura como fonte única da verdade do domínio. Dá para ter uma tabela de auditoria append-only ao lado de um CRUD normal — sem adotar Event Sourcing.

Como se organiza
  • Log (append-only)

    • registro 1

    • registro 2

    • registro 3 ← única posição de escrita

  • Projeções derivadas

    • estado atual

    • índice de busca

A escrita só acontece no fim do log; tudo que é 'estado atual' é derivado dele — descartável e reconstruível a qualquer momento.
02Anatomia

As três responsabilidades

Camada 1/3Escrita

Camada 1

Escrita

anexar no fim — e mais nada

Um único ponto de contenção (a posição final), o que torna a escrita sequencial e rápida. A imutabilidade merece ser imposta no armazenamento: REVOKE de UPDATE/DELETE, tabela particionada por tempo, ou um broker de log como Kafka.

Se violar: um único registro editado 'por emergência' invalida o valor probatório do log inteiro — se um pôde mudar, todos podem ter mudado.

03Implementação

Código

// Um log append-only: a única operação de escrita é "acrescentar no fim".
interface Registro {
  seq: number;            // posição no log — nunca reaproveitada
  tipo: string;
  dados: Record<string, unknown>;
  em: string;             // timestamp ISO
}

const log: Registro[] = [];

function anexar(tipo: string, dados: Record<string, unknown>): Registro {
  const r = { seq: log.length + 1, tipo, dados, em: new Date().toISOString() };
  log.push(r); // sem splice, sem sobrescrever: o passado é intocável
  return r;
}

// Correção não edita o registro errado — SUPERA com um registro novo:
anexar("email-alterado", { usuario: "ana", email: "ana@exemplo.com" });
anexar("email-alterado", { usuario: "ana", email: "ana@empresa.com" }); // vale o mais recente

// O estado atual é uma PROJEÇÃO derivada do log, não a fonte da verdade:
function emailAtual(usuario: string): string | undefined {
  let email: string | undefined;
  for (const r of log) {
    if (r.tipo === "email-alterado" && r.dados.usuario === usuario) {
      email = r.dados.email as string;
    }
  }
  return email; // e o log inteiro responde "quando mudou, de quê, para quê"
}

console.log(emailAtual("ana")); // ana@empresa.com — com a história preservada
04No mundo real

Casos de uso

Trilha de auditoria de um sistema financeiro

Cenário

Compliance exige responder 'quem alterou o limite deste cliente, quando e de quanto para quanto' por cinco anos — e provar que o registro não foi adulterado depois.

Como o padrão entra

Toda mutação relevante grava um registro append-only (ator, antes, depois, timestamp) numa tabela sem UPDATE/DELETE, com hash encadeado opcional para evidenciar adulteração. O CRUD continua normal; a auditoria é um subproduto barato de cada transação.

Trade-off: Escrita dupla em toda operação auditada e um volume de dados que cresce sem teto — retenção e arquivamento em storage frio viram tarefa de rotina.

Log de eventos entre times

Cenário

O time de pedidos precisa avisar estoque, nota fiscal e recomendação a cada venda — sem acoplar seu deploy ao ritmo dos três consumidores.

Como o padrão entra

Pedidos publica eventos num tópico com retenção de dias; cada consumidor mantém seu offset e processa no próprio ritmo. Um consumidor novo (ou um reprocessamento após bug) simplesmente lê o log desde o início — o passado está todo lá.

Trade-off: Consumidores atrasados leem um mundo defasado (consistência eventual), e o schema dos eventos vira contrato público: evoluí-lo sem quebrar leitores antigos exige disciplina de versionamento.

05Cuidado

Armadilhas

  1. O UPDATE 'só desta vez'

    Produção pegando fogo, alguém edita um registro direto no banco para 'corrigir rápido'. A partir daí o log não prova mais nada — auditoria que aceita exceção não é auditoria. Corrija como o modelo manda: um registro novo de retificação, com motivo.

  2. Imutabilidade × LGPD sem plano

    Direito de esquecimento e log imutável colidem de frente. Quem guarda dado pessoal em log precisa decidir no design: criptografar por titular e descartar a chave (crypto-shredding), registrar tombstones, ou manter o pessoal fora do log. Descobrir isso no primeiro pedido de exclusão é tarde.

  3. Crescimento infinito 'para depois'

    Sem política de retenção, o log cobra em disco, em backup e em tempo de replay. Compactação, snapshot e arquivamento não são otimização prematura — são parte do modelo, decididas junto com ele.

06Decisão

Quando usar × quando evitar

Use quando

3
  • Histórico com valor de prova: auditoria, financeiro, saúde, jurídico.
  • Integração via eventos onde consumidores leem em ritmos diferentes.
  • Debug e replicação — reconstruir qualquer estado passado lendo o log.

Evite quando

3
  • Estado quente reescrito milhares de vezes sem ninguém ler a história.
  • Dados pessoais sob exclusão obrigatória, sem crypto-shredding planejado.
  • Times sem apetite para operar retenção e projeções — um CRUD com tabela de auditoria pode bastar.

Testar o que ficou

3 armadilhas de Append-only viram perguntas, com o nome do padrão escondido, misturadas com padrões parecidos.

Onde isto aparece de verdade

Você já usa isto, talvez sem saber o nome.

  • WAL do PostgresO write-ahead log registra cada mudança em sequência antes de aplicá-la; é acréscimo puro, e a recuperação relê o log do ponto seguro.
  • tópicos do KafkaMensagens são anexadas ao fim da partição e nunca alteradas; os consumidores avançam um offset em vez de o produtor apagar o que passou.
  • objetos do gitCommits e blobs são endereçados pelo hash do conteúdo, então gravar é sempre acrescentar um objeto novo — o antigo continua lá, imutável.

O padrão em uma linha

Sem nome de domínio, sem cerimônia. Só a ideia.

// Só append — nunca UPDATE no fato passado.
await log.append({ tipo: "SaldoCreditado", valor: 10 });

O que ele cobra

Nenhum salto a mais entre a chamada e o efeito.

  • O armazenamento só cresce, o que exige compactação, retenção ou arquivamento com o tempo
  • Ler o estado atual pode precisar percorrer ou reduzir o log inteiro sem uma projeção

Não vale se: o dado é naturalmente mutável e sem valor histórico — guardar todas as versões de algo descartável só ocupa espaço.

Onde isto já custou caro

Incidentes reais que este conceito explica.

Onde isto aparece

Continue por aqui