Fundamentos de Design de Schema
Um schema é a planta baixa dos seus dados: quais tabelas existem, quais colunas elas têm e como se relacionam. Um bom design de schema é principalmente senso comum mais alguns hábitos.
- Uma tabela por "coisa." Usuários, posts, pedidos — cada um recebe sua própria tabela.
- Toda linha precisa de um ID único (uma chave primária).
- Conecte tabelas com chaves estrangeiras, não copiando dados de um lado para o outro.
- Escolha tipos honestos. Dinheiro é
decimal, nãofloat. Datas são timestamps, não texto. - Não duplique. Se o email de um usuário mora em cinco tabelas, eventualmente você terá cinco emails diferentes.
Aqui está um schema simples para um blog, do tipo que a IA vai gerar para você:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
author_id UUID NOT NULL REFERENCES users(id),
title TEXT NOT NULL,
body TEXT NOT NULL,
published BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_posts_author ON posts(author_id);
Aquela linha author_id ... REFERENCES users(id) é a chave estrangeira. Ela conecta cada post de volta a exatamente um usuário real, então o próprio banco de dados se recusa a armazenar um post sem um autor válido:
users posts
┌─────────────┐ ┌─────────────┐
│ id (PK) ◀──┼────────────────┤ author_id │ (FK → users.id)
│ email │ um usuário │ id (PK) │
│ created_at │ tem muitos │ title │
└─────────────┘ posts │ body │
│ published │
1 ────────────────────▶ *└─────────────┘
(um) (muitos)
Repare em REFERENCES (uma chave estrangeira ligando um post a um usuário real) e nas restrições NOT NULL e UNIQUE. Esses são guardrails que o banco de dados aplica por você, para que dados ruins não consigam entrar mesmo quando seu código tem um bug.
Uma restrição que vale a pena adicionar deliberadamente é o que acontece quando o "pai" desaparece. Por padrão, apagar um usuário cujos posts ainda o referenciam vai falhar — o que geralmente é o que você quer. Mas você pode ser explícito: REFERENCES users(id) ON DELETE CASCADE apaga os posts também, enquanto ON DELETE RESTRICT bloqueia a exclusão. Escolher de propósito é melhor do que descobrir o comportamento padrão do jeito difícil em produção. O mesmo instinto de "deixe o banco de dados te proteger" se aplica a uma restrição CHECK (CHECK (price >= 0)) — ela recusa dados sem sentido na origem, em vez de confiar que todo caminho de código vai validar.