O banco de dados que nunca esquece: como o Nubank usa o Datomic (e por que ele não faz UPDATE)
Esse post é a versão aprofundada de um vídeo que fiz no meu Instagram, o @bueno_dev, onde posto conteúdo tech todos os dias. Aqui eu vou mais fundo, com código e trade-offs.
Num banco relacional tradicional, quando um cliente muda de endereço, você roda algo assim:
UPDATE clientes SET endereco = 'Rua B' WHERE id = 42;
E pronto: a "Rua A" sumiu. Se amanhã alguém perguntar "qual era o endereço desse cliente no dia 1º de março?", a resposta é "não sei", a não ser que você tenha criado uma tabela de histórico, triggers ou um log de auditoria à parte.
O Datomic, banco que o Nubank usa desde 2014, parte de uma ideia diferente: ele não sobrescreve nada.
Fatos, não linhas
No Datomic, a unidade básica não é uma linha, é um datom, um fato com cinco partes:
[entidade atributo valor transação adicionado?]
[42 :cliente/endereco "Rua A" 1001 true]
Quando o cliente muda de endereço, o Datomic não apaga o fato antigo. Ele registra novos fatos:
[42 :cliente/endereco "Rua A" 1001 true] ; 15/02: passou a valer
[42 :cliente/endereco "Rua A" 2050 false] ; 20/03: deixou de valer
[42 :cliente/endereco "Rua B" 2050 true] ; 20/03: novo valor
Os fatos são imutáveis. O que muda é qual deles vale agora. O "estado atual" é só uma visão sobre o acúmulo de fatos.
Em Clojure, a escrita fica assim:
;; tx 1001
@(d/transact conn [{:cliente/id 42 :cliente/endereco "Rua A"}])
;; tx 2050 — não é um UPDATE destrutivo, é um novo fato
@(d/transact conn [{:cliente/id 42 :cliente/endereco "Rua B"}])
Viagem no tempo de graça
Como nada é apagado, dá para consultar o banco como ele era em qualquer momento:
(-> (d/as-of (d/db conn) #inst "2026-03-01")
(d/pull [:cliente/endereco] [:cliente/id 42]))
;; => {:cliente/endereco "Rua A"}
E dá para ver toda a história de um atributo:
(d/q '[:find ?endereco ?tx ?adicionado
:where [?c :cliente/id 42]
[?c :cliente/endereco ?endereco ?tx ?adicionado]]
(d/history (d/db conn)))
Isso não é uma feature que você precisa montar. É consequência direta do modelo de dados.
"Um Git para os dados"
Edward Wible, cofundador do Nubank, já descreveu o Datomic como um Git para os dados. A analogia funciona bem: cada transação é como um commit, com quando aconteceu e o que mudou. Você nunca reescreve o passado, só adiciona em cima.
$ git log
● tx 2050 · 20/03 endereço: Rua A → Rua B
● tx 1001 · 15/02 endereço: Rua A
Por que isso importa tanto num banco
1. Reconstruir estado a partir dos eventos. Um saldo é o resultado de débitos e créditos aplicados em ordem: +100, -30, -20, +50 → R$ 100. Com o histórico completo e ordenado, dá para reprocessar e chegar ao mesmo resultado, ou descobrir exatamente onde algo divergiu.
2. Auditoria sem esforço extra. Quando um regulador, ou o time de fraude, pergunta "quem mudou o quê e quando?", a resposta já está no banco. Não depende de alguém ter lembrado de logar.
3. Debug de produção. Dá para rodar uma query contra o banco como ele estava no momento do bug, sem montar um snapshot.
Nem tudo são flores (trade-offs)
- O armazenamento cresce sempre. Dado que muda muito (um contador, uma posição de GPS a cada segundo) costuma ser candidato ruim. Para esses casos é comum usar
:db/noHistoryno atributo ou guardar em outro lugar. - "Nunca apagar" vs. LGPD. Às vezes você precisa apagar de verdade. O Datomic tem excision para remover dados permanentemente, mas é uma operação pesada e deliberada, não um
DELETEdo dia a dia. - Escrita centralizada. As escritas passam por um único processo (o transactor), o que simplifica a consistência, mas limita o throughput de escrita comparado a bancos distribuídos.
- Curva de aprendizado. Datalog e Clojure não são o caminho mais comum no mercado brasileiro, e isso pesa na hora de contratar.
A linha do tempo
- 2014: o Nubank escolhe o Datomic.
- 2020: o Nubank adquire a Cognitect, empresa por trás do Datomic e da linguagem Clojure.
- 2023: o Datomic passa a ser gratuito para todo mundo.
Ou seja: dá para testar hoje, sem pagar nada.
Conclusão
A lição que fica, mesmo que você nunca use Datomic: às vezes o histórico vale mais que o valor atual. Vale pensar nisso antes de escrever o próximo UPDATE sobre um dado que alguém vai querer auditar daqui a um ano.
E você, já precisou reconstruir o histórico de um dado que tinha sido sobrescrito? Como resolveu?
Se curtiu, o vídeo com as animações desse conceito (os datoms, a viagem no tempo e o saldo sendo reprocessado) está no @bueno_dev.