Noções Básicas de Modelagem de Dados
8 exemplos para você começar com Modelagem de Dados - 5 básicos e 3 intermediários.
Pré-requisitos
- PostgreSQL 18.4 (ou cluster 17.x compatível) com acesso
psql. - Um banco de dados de rascunho para experimentos de DDL:
CREATE DATABASE modeling_lab;
Exemplos Básicos
1. Esboço Conceitual de Entidade
Nomeie os substantivos do seu domínio antes de escrever DDL.
-- Modelo conceitual (apenas documentação - DDL não executável)
-- Cliente --faz--> Pedido --contém--> ItemPedido
-- Produto --referenciado-por--> ItemPedido- Comece com entidades (Cliente, Pedido) e relacionamentos (faz, contém).
- A cardinalidade vem depois; capture a linguagem de negócios primeiro.
- Este esboço direciona nomes de tabelas, direção de FK e limites de API.
- Compartilhe com produto e backend antes que os arquivos de migração existam.
Relacionado: Modelagem de Entidade-Relacionamento - cardinalidade e opcionalidade
2. Tabela Lógica com Chave Substituta
Mapeie uma entidade para uma tabela com uma chave primária estável.
CREATE TABLE customers (
customer_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL UNIQUE,
display_name text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);GENERATED ALWAYS AS IDENTITYé a chave substituta nativa do PostgreSQL (preferível a serial).UNIQUEememailcodifica uma regra de negócios na camada lógica.timestamptzarmazena instantes absolutos; evitetimestamp without time zonepara horários voltados para o usuário.
Relacionado: Modelagem de Entidade-Relacionamento - nomenclatura e chaves
3. Chave Estrangeira para um-para-Muitos
Linhas filhas referenciam linhas pais; a coluna FK vive no lado "muitos".
CREATE TABLE orders (
order_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES customers (customer_id),
status text NOT NULL DEFAULT 'draft',
placed_at timestamptz
);
CREATE INDEX orders_customer_id_idx ON orders (customer_id);REFERENCES customers (customer_id)impõe integridade referencial no banco de dados.- Indexe a coluna FK (
customer_id) para desempenho de junção e cascata. - O
statuspadrão documenta o ponto de entrada do ciclo de vida no modelo físico.
Relacionado: Modelagem de Entidade-Relacionamento - padrões um-para-muitos
4. Tabela de Junção para Muitos-para-Muitos
Resolva relacionamentos M:N com uma tabela de link dedicada e exclusividade composta.
CREATE TABLE products (
product_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
sku text NOT NULL UNIQUE,
name text NOT NULL
);
CREATE TABLE order_items (
order_id bigint NOT NULL REFERENCES orders (order_id) ON DELETE CASCADE,
product_id bigint NOT NULL REFERENCES products (product_id),
quantity integer NOT NULL CHECK (quantity > 0),
unit_price numeric(12, 2) NOT NULL,
PRIMARY KEY (order_id, product_id)
);PRIMARY KEY (order_id, product_id)composto impede linhas duplicadas por produto por pedido.ON DELETE CASCADEemorder_idremove itens de linha quando um pedido é excluído.- Armazene
unit_priceno momento do pedido (um instantâneo), não apenas o preço atual do catálogo.
Relacionado: Modelagem de Entidade-Relacionamento - resolução M:N
5. Schema como Limite de Contexto Delimitado
Agrupe tabelas relacionadas sob um schema PostgreSQL para espelhar os limites do domínio.
CREATE SCHEMA billing;
CREATE SCHEMA catalog;
CREATE TABLE billing.invoices (
invoice_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL,
issued_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE catalog.products (
product_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
sku text NOT NULL UNIQUE
);- Schemas são namespaces baratos - use-os antes de dividir bancos de dados.
- FKs entre schemas são permitidos, mas sinalizam acoplamento; documente o contrato.
search_pathe permissões por schema controlam quais roles veem qual contexto.
Relacionado: Limites de Schema Orientados a Domínio - contextos delimitados
Exemplos Intermediários
6. Modelo Físico com Restrições CHECK
Codifique invariantes dos quais a aplicação não deve ser a única guardiã.
CREATE TABLE orders (
order_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES customers (customer_id),
status text NOT NULL,
placed_at timestamptz,
CONSTRAINT orders_status_check
CHECK (status IN ('draft', 'placed', 'shipped', 'cancelled')),
CONSTRAINT orders_placed_at_required
CHECK (status = 'draft' OR placed_at IS NOT NULL)
);- Restrições
CHECKsobrevivem a bugs de aplicação e SQL ad-hoc. - Emparelhe enums de status com regras temporais (
placed_atnecessário após o pedido). - Prefira
CHECK+textem vez deENUMdo PostgreSQL quando os valores mudam com frequência.
Relacionado: Estratégia de Evolução de Schema - evoluindo restrições com segurança
7. View de Compatibilidade Durante a Evolução do Schema
Exponha uma interface estável enquanto as colunas físicas se movem por trás das migrações.
ALTER TABLE customers ADD COLUMN legal_name text;
UPDATE customers SET legal_name = display_name WHERE legal_name IS NULL;
CREATE OR REPLACE VIEW customers_v1 AS
SELECT
customer_id,
email,
display_name,
COALESCE(legal_name, display_name) AS name_for_invoices,
created_at
FROM customers;- Views permitem que serviços leiam um contrato estável enquanto as colunas são renomeadas ou divididas.
CREATE OR REPLACE VIEWé uma etapa de expansão de baixo risco em migrações de expandir-contrair.- Deprecie a view somente depois que todos os consumidores migrarem para os novos nomes de colunas.
Relacionado: Estratégia de Evolução de Schema - views retrocompatíveis
8. Comentário de Propriedade no Estilo ADR
Documente quem é o proprietário de uma tabela e se outros serviços podem escrever nela.
COMMENT ON TABLE billing.invoices IS
'Owner: billing-service. Writes: billing-service only. '
'Reads: reporting-replica, finance-batch. '
'ADR: shared-db-with-schema-isolation (2024-06).';- Metadados
COMMENT ONsobrevivem em dumps e na saída de\d+. - Vincule comentários a um ADR ao debater bancos de dados únicos vs. por serviço.
- Comentários de propriedade evitam escritas "úteis" entre serviços que corrompem invariantes.
Relacionado: ADR: Banco de Dados Único vs. Banco de Dados por Serviço - compensações de propriedade de dados
Versões da Stack: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x e PostGIS 3.5+.