Juliana Assalti
Voltar para o blog
2 min de leitura

Modelagem de dados: a decisão mais cara de reverter

Framework, linguagem e interface se trocam. O modelo de dados, depois de em produção com dados reais, resiste — e cada feature nasce brigando com ele. Por que essa é a decisão que mais merece cuidado no início.

Quase toda decisão técnica é reversível a um custo tolerável. Trocar de framework dá trabalho; trocar de linguagem, mais ainda; refazer uma interface é rotina. O modelo de dados é a exceção: uma vez em produção, com dados reais de gente real, ele resiste à mudança como nenhuma outra parte do sistema.

O custo não está no diagrama — está no que já foi gravado. Migrar uma tabela viva significa script de migração reversível, integridade a preservar, janela de indisponibilidade a negociar e código acoplado a ajustar em cascata. Errar cedo é barato; errar depois de mil registros é caro; errar depois de um milhão vira projeto.

Modele o domínio, não a tela

O erro mais comum é moldar o schema em torno da interface atual — uma tabela por tela, um campo por input. Interface muda a cada trimestre; o domínio, não. Modelar o domínio é capturar as entidades reais do negócio e as regras que valem sempre (as invariantes), independentemente de como a tela mostra isso hoje.

  • Pergunte o que é verdade sempre, não o que a tela precisa agora.
  • Nomeie as entidades como o negócio as nomeia, não como o CRUD as expõe.
  • Deixe o banco garantir o que é invariante — chave, unicidade, integridade referencial — em vez de confiar só no código da aplicação.

Limites de serviço seguem os donos do dado

Quando o sistema cresce e a pergunta vira “onde dividir”, a resposta costuma estar no dado, não no código. Um serviço deve ser dono do que escreve. Dois serviços disputando a mesma tabela não são dois serviços — são um só, com a fronteira desenhada no lugar errado, e cada deploy de um quebra o outro.

Um exemplo concreto: em vez de deixar a aplicação garantir que todo pedido tem um cliente válido e um total não-negativo, a própria modelagem fecha essa porta.

CREATE TABLE pedido (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  cliente_id  uuid NOT NULL REFERENCES cliente(id),
  total_cents integer NOT NULL CHECK (total_cents >= 0),
  criado_em   timestamptz NOT NULL DEFAULT now()
);

A constraint não é burocracia: é a invariante escrita onde ninguém consegue contornar. Código de aplicação esquece de validar; o banco, não. Um pedido sem cliente ou com total negativo simplesmente não existe — e isso vale para todos os caminhos de escrita, inclusive o script de migração apressado de uma sexta à noite.

Nenhum modelo nasce perfeito, e não é disso que se trata. Trata-se de gastar o cuidado onde ele rende mais: uma boa modelagem faz feature nova custar pouco; uma ruim faz cada feature brigar com o passado. É a fundação — e fundação se acerta antes de subir a parede.