Um diagrama ER é onde o projeto de um banco de dados é conferido antes de virar migrations. Uma lista de tabelas diz o que existe. O diagrama mostra como as tabelas dependem umas das outras: qual linha precisa de qual, o que pode faltar e onde se esconde um muitos para muitos.
Este guia projeta o banco de dados de uma pequena loja virtual, de requisitos em linguagem simples até um diagrama entidade-relacionamento pronto, e explica a notação conforme ela aparece. A loja é só o exemplo; o método funciona para qualquer esquema.
Quando você precisa de um diagrama ER
Uma lista de tabelas basta quando o esquema tem um punhado de tabelas e um só time trabalha nele. Desenhe o diagrama quando pessoas de fora desse time precisarem entender os dados, quando você revisar um projeto antes de escrever migrations ou quando o esquema tiver crescido a ponto de ninguém mais conseguir guardá-lo de cabeça.
Há duas notações comuns. A notação de Chen, do artigo de Peter Chen de 1976 que apresentou o modelo entidade-relacionamento, desenha entidades como retângulos e relacionamentos como losangos. Ela serve bem à modelagem conceitual. A notação pé de galinha (crow's foot) desenha tabelas como caixas e coloca a cardinalidade nas pontas das linhas. Costuma ser atribuída a Gordon Everest, em 1976, e é a notação usada pela maioria das ferramentas de modelagem de banco de dados. Este guia usa pé de galinha.
Passo 1: requisitos em frases simples
Escreva primeiro as regras como frases. Cada uma vira uma tabela, uma coluna ou uma linha.
- Um cliente se cadastra com um endereço de e-mail e pode salvar vários endereços.
- Um cliente faz pedidos. Cada pedido é entregue em um dos endereços do cliente.
- Um pedido contém um ou mais produtos, cada um com uma quantidade e o preço pago.
- Cada produto pertence a uma categoria.
- Um pedido pode ter várias tentativas de pagamento, e a maioria tem uma que deu certo.
Passo 2: entidades e chaves
Os substantivos viram entidades: clientes, endereços, pedidos, itens do pedido, produtos, categorias e pagamentos. Cada uma ganha uma chave primária. Use chaves substitutas (uma coluna identity) na maioria das tabelas e chaves naturais únicas onde o negócio já tem uma: o e-mail de um cliente, o SKU de um produto.
Os itens do pedido são a exceção. Um item do pedido não tem identidade própria; ele é a ligação entre um pedido e um produto. A chave primária é o par (order_id, product_id).
Passo 3: relacionamentos e cardinalidade
Cada chave estrangeira é uma linha. Os símbolos em cada ponta dizem quantas linhas podem ficar daquele lado:
| Ponta da linha | Lê-se | Exemplo da loja |
|---|---|---|
| Duas barras | Exatamente um | Cada pedido é feito por exatamente um cliente |
| Círculo e barra | Zero ou um | Um pedido tem zero ou uma mensagem de presente, se você incluir uma |
| Barra e pé de galinha | Um ou muitos | Um pedido tem um ou muitos itens |
| Círculo e pé de galinha | Zero ou muitos | Um cliente tem zero ou muitos pedidos |
Leia um relacionamento a partir de uma tabela, ao longo da linha, até o símbolo na outra ponta: "um cliente faz zero ou muitos pedidos", e de volta: "cada pedido é feito por exatamente um cliente".
Uma chave estrangeira NOT NULL significa "exatamente um" do lado referenciado; uma que aceita NULL significa "zero ou um". O "um ou muitos" do outro lado é uma regra de negócio, não algo que uma chave estrangeira garanta, então escreva essa regra no diagrama, onde os leitores vão vê-la.
Passo 4: o muitos para muitos
Pedidos e produtos têm um relacionamento muitos para muitos: um pedido tem muitos produtos, e um produto aparece em muitos pedidos. Bancos relacionais modelam isso com uma tabela no meio. Aqui essa tabela já existe por um motivo melhor do que encanamento: order_items guarda a quantidade e o preço unitário no momento da compra, fatos que pertencem ao par e a nenhum dos lados sozinho.
Passo 5: decisões que vale registrar
Algumas escolhas não aparecem na lista de colunas e causam bugs quando ninguém as conhece:
- Retrato do preço.
order_items.unit_pricecopia o preço do produto no momento da compra. Mudar um preço depois não pode alterar pedidos antigos. - Endereços não são editados no lugar. Quando um pedido aponta para um endereço, editar essa linha reescreveria para onde os pedidos antigos foram. Uma edição cria uma nova linha de endereço.
- Pagamentos são tentativas. Uma linha por tentativa, com status, para que pagamentos que falharam e foram refeitos continuem visíveis.
- Dinheiro é
numeric. A documentação do PostgreSQL recomendanumericpara valores monetários porque ele é exato.
Passo 6: o DDL
As duas tabelas centrais no PostgreSQL:
CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES customers (id),
shipping_address_id bigint NOT NULL REFERENCES addresses (id),
status text NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled')),
currency char(3) NOT NULL,
total numeric(12,2) NOT NULL CHECK (total >= 0),
placed_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE order_items (
order_id bigint NOT NULL REFERENCES orders (id) ON DELETE CASCADE,
product_id bigint NOT NULL REFERENCES products (id),
quantity integer NOT NULL CHECK (quantity > 0),
unit_price numeric(12,2) NOT NULL, -- copied from products.price at checkout
PRIMARY KEY (order_id, product_id)
);
CREATE INDEX ON orders (customer_id);
CREATE INDEX ON order_items (product_id);
O PostgreSQL não cria sozinho um índice para uma coluna de chave estrangeira, então as duas linhas CREATE INDEX importam para os joins e para exclusões nas tabelas referenciadas. order_items.order_id é coberta pela chave primária, cujo índice começa por ela. ON DELETE CASCADE diz que um item não pode sobreviver ao seu pedido.
O diagrama
Conteúdo do diagrama
- customers
- orders
- payments
- addresses
- order_items
- products
- categories
- belongs to
- placed by
- ships to
- pays for
- line of
- for
- in
Como ler:
- Clientes (no canto superior esquerdo) têm zero ou muitos endereços e fazem zero ou muitos pedidos.
- Pedidos (no centro) vão para exatamente um endereço, contêm um ou muitos itens do pedido e têm zero ou muitos pagamentos.
- Itens do pedido ligam cada pedido a produtos, e cada produto pertence a exatamente uma categoria.
- A nota traz as decisões do passo 5, ao lado das tabelas que elas afetam.
Como desenhar um diagrama ER legível
- Nomeie os relacionamentos com verbos. "feito por", "entregue em", "paga" transformam linhas em frases.
- Marque as chaves nos atributos. Os marcadores PK e FK mostram de relance a qual coluna uma linha pertence.
- Agrupe as tabelas por domínio. Clientes e endereços, pedidos e pagamentos, o catálogo. Mantenha cada grupo próximo para que as linhas fiquem curtas.
- Deixe o ruído de fora. Colunas de auditoria como
created_byeupdated_at, flags de exclusão lógica em todas as tabelas e tabelas técnicas acrescentam linhas sem acrescentar entendimento. - Desenhe a tabela do muitos para muitos. Escondê-la atrás de uma linha direta entre pedidos e produtos esconde também a quantidade e o retrato do preço.
- Mostre as regras de negócio que o esquema não expressa. "Um ou muitos" itens por pedido e "endereços não são editados" são regras que todo mundo precisa conhecer.
Mantenha o diagrama em sincronia com o esquema
Um diagrama ER desenhado à mão fica desatualizado depois da próxima migration. Em vez disso, gere um primeiro rascunho a partir do esquema. A ferramenta gratuita SQL para diagrama ER lê instruções CREATE TABLE ou um pg_dump inteiro e desenha um card por tabela, com marcadores PK, FK e UK e uma linha por chave estrangeira; também há ferramentas para PostgreSQL, Prisma e DBML. As pontas pé de galinha geradas seguem as colunas: uma chave estrangeira NOT NULL é obrigatória e uma que aceita NULL é opcional. Regras que o SQL não contém, como "um ou muitos" itens, você acrescenta à mão.
Monte no Archboard
- Entre com Google ou GitHub e crie um diagrama.
- Para começar pelo esquema, cole as instruções CREATE TABLE na ferramenta SQL para diagrama ER e selecione Abrir no Archboard. Para desenhar à mão, adicione uma Entidade do pack Entidade-relacionamento para cada tabela, com um atributo por linha e marcadores PK e FK.
- Conecte cada chave estrangeira à tabela que ela referencia e configure as pontas das linhas com marcadores pé de galinha: exatamente um na tabela referenciada, zero ou muitos no lado da chave estrangeira e um ou muitos onde uma regra de negócio exigir.
- Rotule cada linha com um verbo, como "feito por" ou "entregue em".
- Adicione um bloco Markdown do pack Básico com as decisões de design.
O exemplo usa 8 nós de diagrama e fica dentro do plano Free. Para ver como um esquema se encaixa no resto de um sistema, veja o exemplo de diagrama de design de sistemas.
Perguntas frequentes
Qual é a diferença entre um diagrama ER e um diagrama de esquema de banco de dados? Na prática, eles se sobrepõem. Um diagrama ER pode ser conceitual e mostrar entidades e relacionamentos antes de as tabelas existirem; um diagrama de esquema mostra as tabelas, colunas e chaves reais. O diagrama pé de galinha deste guia é as duas coisas.
Toda tabela deve ter uma chave substituta?
A maioria das tabelas se beneficia de uma, porque chaves naturais mudam. Tabelas de ligação como order_items costumam usar o par de chaves estrangeiras como chave primária.
Como mostro um relacionamento muitos para muitos? Com a tabela do meio e duas linhas um para muitos. Dê à tabela do meio o nome do que ela representa, como itens do pedido, e não o das duas tabelas que ela liga.
Desenho todas as colunas? Desenhe as chaves e as colunas que explicam o projeto. Deixe de fora colunas de auditoria e técnicas, a menos que o diagrama seja sobre elas.
Chen ou pé de galinha? Chen para conversas conceituais com pessoas que não pensam em tabelas; pé de galinha para tudo o que vai virar um banco de dados.