📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Módulo 08: Bancos de Dados SQL e NoSQL. Recomendado revisar antes de começar.
v1.0 — Normalização, Triggers, Constraints e Integridade Transacional
Trilha de Engenharia de Dados & Bancos de Dados — Projeto 1 de 4
- ➡️ v1 (este): Modelagem Relacional 3FN · Constraints · Triggers PL/pgSQL · PostgreSQL 16
- v2: Modelagem NoSQL · Aggregation Pipelines · MongoDB 7.0
- v3: Estratégias de Cache · Cache-Aside · Invalidação · Redis 7.2
- v4: DBA Enterprise · EXPLAIN ANALYZE · Particionamento · PgBouncer · RLS
🎓 Nível Profissional Simulado: Desenvolvedor Júnior / Engenheiro de Dados Pleno. Na vida real, 80% dos bugs em produção ocorrem por inconsistência no banco (valores negativos, CPFs duplicados, estoque divergente). Um engenheiro sênior projeta o banco para ser auto-consistente, garantindo que nenhuma aplicação externa consiga corromper o estado dos dados.
Construir do zero o esquema relacional de um Marketplace Corporativo aplicando rigorosamente a Terceira Forma Normal (3FN), chaves estrangeiras com restrições atômicas, colunas geradas e uma trigger procedural em PL/pgSQL para atualização automática de estoque e recálculo do valor total dos pedidos em eventos de inserção, atualização e exclusão de itens.
erDiagram
CATEGORIAS ||--o{ PRODUTOS : "categoriza (1:N)"
CLIENTES ||--o{ PEDIDOS : "realiza (1:N)"
PEDIDOS ||--|{ ITENS_PEDIDO : "contém (1:N)"
PRODUTOS ||--o{ ITENS_PEDIDO : "composto_por (1:N)"
CLIENTES {
int id PK
string nome
string cpf UK "Formato 000.000.000-00"
string email UK
string telefone
timestamp data_cadastro
}
CATEGORIAS {
int id PK
string nome UK
string slug UK
boolean ativa
}
PRODUTOS {
int id PK
int categoria_id FK
string nome
string sku UK
decimal preco "CHECK > 0"
int estoque_atual "CHECK >= 0"
int estoque_minimo
boolean ativo
timestamp criado_em
}
PEDIDOS {
int id PK
int cliente_id FK
timestamp data_pedido
string status "CHECK PENDENTE|PAGO|ENVIADO|CANCELADO"
decimal valor_total "Atualizado via Trigger"
}
ITENS_PEDIDO {
int pedido_id PK,FK
int produto_id PK,FK
int quantidade "CHECK > 0"
decimal preco_unitario
decimal subtotal "GENERATED ALWAYS AS"
}
“Estamos com sérios problemas no nosso e-commerce atual. O vendedor consegue cadastrar produto com preço zero, o estoque frequentemente fica negativo quando entram dois pedidos juntos, e o valor total do pedido às vezes não bate com a soma dos itens porque um desenvolvedor júnior esqueceu de calcular o subtotal no backend. Precisamos de um banco de dados profissional onde essas regras sejam garantidas pelo próprio SGBD!”
| ID | Tipo | Descrição | Origem no Briefing |
|---|---|---|---|
| RF01 | Funcional | O sistema deve impedir cadastro de CPFs e e-mails duplicados. | “evitar contas duplicadas” |
| RF02 | Funcional | O preço do produto deve ser estritamente maior que zero (preco > 0). |
“produto com preço zero” |
| RF03 | Funcional | O estoque nunca pode ser negativo (estoque_atual >= 0). |
“estoque fica negativo” |
| RF04 | Funcional | O valor total do pedido e o estoque devem ser atualizados automaticamente ao inserir, alterar ou remover itens. | “subtotal não bate com a soma” |
| RNF01 | Não-Funcional | Normalização estrita em Terceira Forma Normal (3FN) sem redundâncias. | Engenharia de Software |
| RNF02 | Não-Funcional | Atomicidade e consistência transacional padrão ACID no PostgreSQL 16. | Confiabilidade SGBD |
| ID | User Story | Prioridade |
|---|---|---|
| US01 | Como cliente, quero me cadastrar com CPF e e-mail validados sem duplicidade. | Alta |
| US02 | Como administrador, quero cadastrar categorias e produtos com SKU único e preço positivo. | Alta |
| US03 | Como comprador, quero adicionar itens ao pedido tendo o subtotal e estoque atualizados no mesmo instante. | Alta |
| US04 | Como analista financeiro, quero emitir relatórios de faturamento consolidado por categoria. | Média |
# Branch da funcionalidade
git checkout -b feature/US03-triggers-estoque
# Subir o ambiente PostgreSQL isolado
docker-compose up -d
# Validar se o schema foi carregado com sucesso
docker exec -it db_sql_postgres psql -U admin -d marketplace_db -c "\dt"
sql/01_schema.sql)CREATE TABLE produtos (
id SERIAL PRIMARY KEY,
categoria_id INT NOT NULL,
nome VARCHAR(150) NOT NULL,
sku VARCHAR(30) NOT NULL UNIQUE,
preco NUMERIC(10, 2) NOT NULL,
estoque_atual INT NOT NULL DEFAULT 0,
CONSTRAINT fk_produto_categoria FOREIGN KEY (categoria_id)
REFERENCES categorias(id) ON DELETE RESTRICT,
CONSTRAINT chk_preco_positivo CHECK (preco > 0),
CONSTRAINT chk_estoque_nao_negativo CHECK (estoque_atual >= 0)
);
CREATE OR REPLACE FUNCTION fn_atualizar_estoque_e_total()
RETURNS TRIGGER AS $$
DECLARE
v_pedido_id INT;
BEGIN
IF (TG_OP = 'DELETE') THEN
v_pedido_id := OLD.pedido_id;
UPDATE produtos SET estoque_atual = estoque_atual + OLD.quantidade WHERE id = OLD.produto_id;
ELSIF (TG_OP = 'UPDATE') THEN
v_pedido_id := NEW.pedido_id;
UPDATE produtos SET estoque_atual = estoque_atual + OLD.quantidade - NEW.quantidade WHERE id = NEW.produto_id;
ELSIF (TG_OP = 'INSERT') THEN
v_pedido_id := NEW.pedido_id;
UPDATE produtos SET estoque_atual = estoque_atual - NEW.quantidade WHERE id = NEW.produto_id;
END IF;
UPDATE pedidos
SET valor_total = (SELECT COALESCE(SUM(subtotal), 0) FROM itens_pedido WHERE pedido_id = v_pedido_id)
WHERE id = v_pedido_id;
RETURN CASE WHEN TG_OP = 'DELETE' THEN OLD ELSE NEW END;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_atualizar_item_pedido
AFTER INSERT OR UPDATE OR DELETE ON itens_pedido
FOR EACH ROW
EXECUTE FUNCTION fn_atualizar_estoque_e_total();
No seu editor/IDE, abra a pasta deste projeto (File > Open Folder) ou navegue via terminal:
cd db_sql_01_modelagem_relacional
docker-compose up -d
# Executar a suíte de testes automatizados:
python -m unittest tests/test_modelagem_relacional.py
[!TIP] Dica para execução a partir da raiz do repositório: Se você abriu o repositório completo no VS Code, basta navegar até a pasta antes de executar:
cd catalogo_projetos/db_sql_01_modelagem_relacional
-- 1. Teste de Violação de Preço Negativo (Deve FALHAR com Check Constraint)
INSERT INTO produtos (categoria_id, nome, sku, preco) VALUES (1, 'Teste Invalido', 'SKU-001', -10.00);
-- 2. Teste da Trigger: Inserir Pedido e Item
INSERT INTO pedidos (cliente_id) VALUES (1);
INSERT INTO itens_pedido (pedido_id, produto_id, quantidade, preco_unitario)
VALUES (1, 1, 2, 1450.00);
-- 3. Validar se o total do pedido foi para R$ 2900.00 e o estoque baixou 2 unidades
SELECT valor_total FROM pedidos WHERE id = 1;
SELECT estoque_atual FROM produtos WHERE id = 1;
A arquitetura abaixo demonstra o fluxo de persistência, otimização e isolamento dos componentes na subpasta sql/:
flowchart TD
subgraph DDL["Modelagem & Schema (DDL)"]
S1["Tabelas / Coleções"] --> S2["Constraints & Índices"]
end
subgraph DML["Regras & Automação (DML / PL)"]
R1["Triggers / Aggregations"] --> R2["Procedimentos Transacionais"]
end
subgraph Perf["Otimização & Armazenamento"]
P1["Planos EXPLAIN / Partições"] --> P2["Garantia de ACID / TTL"]
end
subgraph Homolog["Homologação & Qualidade"]
Q1["Docker Compose & Scripts"] --> Q2["Checkpoints CP-01..05 PASS"]
end
DDL --> DML
DML --> Perf
Perf --> Homolog
style DDL fill:#e3f2fd,stroke:#1976d2,stroke-width:1.5px
style DML fill:#fffde7,stroke:#fbc02d,stroke-width:1.5px
style Perf fill:#e8f5e9,stroke:#388e3c,stroke-width:1.5px
style Homolog fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1.5px
| ID | Persona | História de Usuário | Critério de Aceite | Prioridade |
|---|---|---|---|---|
| US-01 | DBA / Engenheiro de Dados | Desejo esquema relacional em 3FN para marketplace corporativo | Tabelas normalizadas sem anomalias de inserção, atualização ou exclusão | Alta |
| US-02 | Desenvolvedor Backend | Desejo constraints CHECK e UNIQUE garantindo consistência na raiz | Impedir CPFs duplicados, preços nulos e quantidades de estoque negativas | Alta |
| US-03 | Analista de Operações | Desejo trigger PL/pgSQL sincronizando estoque e total de pedidos | Execução atômica em eventos de INSERT, UPDATE e DELETE de itens | Alta |
| US-04 | Administrador de Banco | Desejo ambiente conteinerizado via Docker Compose para testes imediatos | Subida determinística do PostgreSQL 16 com scripts de seed automatizados | Média |
| US-05 | Auditor de Qualidade | Desejo suíte de testes unitários validando integridade referencial | Testes em Python com psycopg validando violações de constraints esperadas | Média |
| ID | Descrição do Requisito | Criticidade |
|---|---|---|
| RF-01 | Esquema relacional em PostgreSQL normalizado até a 3ª Forma Normal | Alta |
| RF-02 | Restrições atômicas de integridade (PK, FK ON DELETE, CHECK, UNIQUE) | Alta |
| RF-03 | Trigger procedural PL/pgSQL para atualização automática de saldo de estoque | Alta |
| RF-04 | Subida automatizada do banco com docker-compose e migrations em sql/ | Média |
| RF-05 | Suíte de testes automatizados com cobertura de regras de consistência | Média |
| ID | Especificação Arquitetural | Criticidade |
|---|---|---|
| RNF-01 | Garantia formal de propriedades ACID em todas as mutações relacionais | Alta |
| RNF-02 | Latência de execução de triggers inferior a 10ms por transação | Alta |
| RNF-03 | Compatibilidade estrita com PostgreSQL 16+ | Alta |
| RNF-04 | Independência de frameworks externos para manutenção de regras no banco | Média |
| RNF-05 | Organização modular dos scripts na pasta sql/ | Alta |
| User Story | Requisito Funcional | Requisito Não-Funcional | Módulo / Artefato de Código | Status de Homologação |
|---|---|---|---|---|
| US-01 | RF-01 |
RNF-01, RNF-03 |
sql/01_schema.sql |
Testado |
| US-02 | RF-02 |
RNF-01, RNF-04 |
sql/01_schema.sql |
Testado |
| US-03 | RF-03 |
RNF-01, RNF-02 |
sql/02_triggers.sql |
Testado |
| US-04 | RF-04 |
RNF-03, RNF-05 |
docker-compose.yml |
Testado |
| US-05 | RF-05 |
RNF-01, RNF-05 |
tests/test_modelagem_relacional.py |
Testado |
| Checkpoint | Escopo da Validação | Artefato Verificado | Status |
|---|---|---|---|
| CP-01 | Subida Conteinerizada do Ambiente | docker-compose.yml |
PASS ✅ |
| CP-02 | Criação de Estrutura & Schemas | sql/ |
PASS ✅ |
| CP-03 | Automação Transacional & Regras | Triggers / Queries |
PASS ✅ |
| CP-04 | Otimização de Performance & Índices | Indexes / Tuning |
PASS ✅ |
| CP-05 | Homologação com Suíte de Testes | tests/ |
PASS ✅ |