Em sistemas modernos, a restauração de dados é uma demanda real, seja por erro do usuário, auditoria ou suporte. O objetivo deste artigo é apresentar o conceito do Soft Delete, assim como suas principais estratégias de forma interativa, para que você possa testar e escolher a melhor abordagem para o seu próximo projeto.
O Problema
Imagine um cenário comum onde um usuário exclui um registro importante por engano. Pode ser um admin que clicou no botão errado, um cliente removendo dados que depois viram críticos, ou alguém limpando o que achou que era temporário. Quando não existe uma estratégia de retenção ou recuperação desses dados, esse erro simples vira uma crise real, onde você precisa parar tudo, restaurar backup em ambiente isolado, caçar registros manualmente e reinserir o que for possível, com risco de perder tudo que foi criado depois do último snapshot do banco.
Esse cenário é um exemplo perfeito de Hard Delete em tabelas críticas. Hard Delete é a exclusão física do registro, normalmente feita com um comando DELETE direto na tabela. Resumindo o dado é realmente removido do banco e do disco. Não há volta imediata, não há trilha de auditoria por padrão e, sem backup externo, não há como recuperar.
Aqui esta um exemplo de Hard Delete:
Exemplo de hard delete em uma tabela de tarefas
Selecione alguma linha
tasks
SQL Console
readonlyEssa abordagem é perigosa para tabelas críticas, mas é a ideal (e nativa) para dados secundários, registros temporários ou cadastros rápidos que não impactam o negócio.
A Solução
O Soft Delete trata a exclusão como um estado lógico, não físico. Para o usuário, o dado sumiu, mas para o banco ele apenas foi marcado como oculto. Existem duas principais formas de se implementar isso, que são o que chamamos de Soft Delete Patterns.
Logical Delete
O primeiro Soft Delete Pattern é o Logical Delete, sendo o que tem a abordagem mais simples e comum. Consiste em adicionar uma coluna na própria tabela para sinalizar o estado do registro.
Siga as etapas abaixo para entender o fluxo de um Logical Delete
Exemplo de Logical Delete em uma tabela de tarefas
Selecione alguma linha
tasks
SQL Console
readonlyComo podemos observar, em vez de remover fisicamente o registro, apenas atualizamos a coluna 'archived_at'. Dessa forma, conseguimos distinguir quem está ativo e quem já foi arquivado ou deletado.
Para implementar o Logical Delete, basta adicionar uma nova coluna em sua tabela:
SQL SCRIPT
readonlyEsse campo pode ser um booleano ou um timestamp. O importante é que ele indique claramente se o registro está ativo ou não.
O problema surge em larga escala. Milhões de registros desativados continuam ocupando espaço e sujando seus índices na tabela principal.
Aqui esta um desafio comum do Logical Delete:
Exemplo do problema de tabela poluída com logical delete
Execute um select para exibir todas as tarefas ativas
tasks
SQL Console
readonlyEm 99% do tempo você só quer os dados ativos, mas é obrigado a carregar o peso de milhões de registros mortos e lembrar de filtrá-los em toda query.
Shadow Table
O segundo Soft Delete Pattern é o Shadow Table, que consiste numa solução mais complexa para resolver o inchaço da tabela principal. Aqui, em vez de marcar o registro, nós o movemos para uma tabela secundária de arquivo.
Siga as etapas abaixo para entender o fluxo do Shadow Table
Exemplo do padrão shadow table para recuperação de dados
Selecione alguma linha
tasks
archives
SQL Console
readonlyComo podemos observar, ao deletar um registro da tabela 'tasks', ele é automaticamente movido para a tabela 'archives' através de um trigger no banco de dados.
Para implementar o Shadow Table, a complexidade aumenta, primeiro devemos criar a tabela 'archives' em nosso banco de dados:
SQL SCRIPT
readonlyAgora devemos criar a função que lidará com a lógica de arquivamento:
How to implement Shadow Table
readonlyEssa função é responsável por pegar o registro deletado e inserir ele na tabela 'archives'.
Agora basta criarmos os triggers para as tabelas críticas. Assim, elas ficam protegidas contra o Hard Delete. No exemplo abaixo, o padrão de Shadow Table será aplicado em três tabelas de exemplo: 'users', 'establishments' e 'appointments'.
How to create Shadow Table Triggers
readonlyNada é de graça. Enquanto o Logical Delete exige apenas um UPDATE, restaurar dados em uma Shadow Table exige queries de INSERT INTO e SELECT mais complexas, especialmente se houver relacionamentos de chaves estrangeiras envolvidos.
No exemplo abaixo, temos 3 exemplos de situações onde você deve recuperar dados deletados.
Exemplo de restauração de dados arquivados
archives
Mateus (mateus@email.com) acionou o suporte informando que apagou por engano o estabelecimento 'Downtown Barbershop' e precisa recuperar o registro.
A cliente Fernanda Lima solicitou a recuperação da conta removida acidentalmente durante uma limpeza manual de dados.
O suporte recebeu pedido para restaurar um agendamento cancelado por engano, mantendo o histórico para auditoria.
SQL Console
readonlyO trade-off é direto, recuperar dado aqui não é um simples UPDATE, normalmente envolve buscar contexto, remontar dependências e executar uma sequência de queries com mais cuidado.
Os 3 SELECTs deixam claro por que vale modelar record_id e caused_by_id como JSONB. Isso abre espaço para registros com chave composta sem forçar o schema da 'archives' a cada novo caso. As colunas caused_by_table e caused_by_id também melhoram muito a rastreabilidade do cascade delete, porque tornam explícita a origem do evento.
Conclusão: Qual escolher?
Na prática, a melhor estratégia de exclusão depende muito do momento do produto e do nível de maturidade da arquitetura. Não existe resposta única, existe escolha consciente por contexto.
Para POCs e fases iniciais, o Logical Delete costuma fazer mais sentido. Ele entrega segurança para recuperar dados com implementação rápida, baixo atrito e sem elevar cedo demais a complexidade do projeto.
Para MVPs robustos, pilotos e produto final em produção, o Shadow Table pode ser bem mais vantajoso. Apesar do custo maior de implementação, ele ajuda a manter a tabela principal mais limpa, melhora previsibilidade de performance e organiza melhor a estratégia de arquivamento.
Existem outros patterns de soft delete além dos citados aqui. Neste artigo, eu trouxe os mais conhecidos que já usei na prática, justamente para compartilhar decisões que testei em cenários reais.
Menção honrosa a Alex Buchanan, criador do blog atlas9.dev, que me inspirou a montar esse artigo.
