📐 CAPÍTULO 05: MODELO RELACIONAL E MODELAGEM CONCEITUAL


🎯 Objetivos de Aprendizagem

Ao final deste capítulo (estimativa: 2 horas de estudo autoguiado), você será capaz de:

  • 🔹 Compreender os fundamentos do Modelo Entidade-Relacionamento (MER) de Peter Chen e do Modelo Relacional de Edgar Codd.
  • 🔹 Identificar Entidades Fortes, Entidades Fracas, Atributos Simples, Compostos e Multivalorados.
  • 🔹 Modelar cardinalidades fundamentais: Um para Um (1:1), Um para Muitos (1:N) e Muitos para Muitos (N:N).
  • 🔹 Construir diagramas relacionais e conceituais utilizando Mermaid e Draw.io.

O "coração" da engenharia de dados moderna é baseado na matemática. Criado na década de 70 por Edgar F. Codd (cientista da IBM), o Modelo Relacional revolucionou o mundo ao organizar informações de forma previsível e segura. 🛡️🧩

🏢 O Cenário Prático (Seu Desafio)

Você assumiu a área de Arquitetura de Dados da TecProExpress. A equipe de negócios enviou um documento textual gigante descrevendo como eles querem que o sistema funcione. Os programadores não sabem por onde começar a codificar as tabelas.

"Seu desafio é ser a ponte de comunicação. Você precisa traduzir as necessidades do negócio (Mundo Real) em um diagrama visual (Mini-mundo) e depois em código SQL estruturado."


🧠 Fundamentos: O Dicionário Relacional

No mercado profissional, evitamos termos amadores. Um Arquiteto de Elite domina a nomenclatura técnica.

Nome ComercialNomenclatura CientíficaO que representa na prática?
TabelaRelaçãoEstrutura que guarda entidades (ex: cliente).
Linha / RegistroTuplaUma ocorrência específica (ex: O cliente João).
Coluna / CampoAtributoPropriedade (ex: nome, cpf).
Tipo de DadoDomínioRegras de formato permitidas (ex: INT, VARCHAR).

📊 Anatomia de uma Tabela (Relação)

erDiagram
    PRODUTO {
        int id_produto PK
        string nome
        decimal preco
    }

🔍 Detalhamento das Regras de Ouro:

  1. Atomicidade: Cada célula deve conter apenas um único valor indivisível. (Nada de guardar dois telefones na mesma coluna).
  2. Unicidade: Não existem linhas 100% iguais (garantido pela PK - Primary Key).
  3. O Valor NULL: Representa ausência de informação. Atenção: NULL não é zero e nem um texto vazio ("").

📖 Exemplo Guiado: Criando o Dicionário (DDL -> DML)

A melhor forma de entender os domínios é aplicando restrições no código.

🛠️ Código do Exemplo

-- PASSO 1: DDL (Definindo Relação, Atributos e Domínios)
CREATE TABLE fornecedor (
    id INT PRIMARY KEY,
    nome VARCHAR(100) NOT NULL,
    status_ativo BOOLEAN
);

-- PASSO 2: DML (Inserindo Tuplas)
INSERT INTO fornecedor (id, nome, status_ativo) VALUES (1, 'TecProExpress', TRUE);
INSERT INTO fornecedor (id, nome, status_ativo) VALUES (2, 'Fornecedor Beta', NULL);

🔍 Detalhamento do Código:

  • VARCHAR(100): O domínio restringe o tamanho do atributo "nome" a 100 letras.
  • NULL: O segundo INSERT usa NULL porque ainda não sabemos o status do Fornecedor Beta.

📐 O Ciclo de Vida da Modelagem

O processo de traduzir o mundo real para tabelas ocorre em 3 fases:

  1. 🧠 Modelo Conceitual: Foca na regra de negócio. Desenho em alto nível (Diagrama Entidade-Relacionamento - DER) que o cliente consegue entender.
  2. ⚙️ Modelo Lógico: Traduz o diagrama para tabelas (com PKs e FKs), mas ainda sem código específico.
  3. 💻 Modelo Físico: O script de criação (SQL DDL) que roda dentro do SGBD (MySQL/Postgres).

📊 O Fluxo de Abstração

flowchart TD
    REAL["🌍 Mundo Real"] --> ABS{"🔍 Abstração"}
    ABS --> MINI["🗺️ Modelo Conceitual"]
    MINI --> LOG["📐 Modelo Lógico"]
    LOG --> FIS["💻 Modelo Físico (SQL)"]

🛠️ Prática Obrigatória: Abstração Inicial

Cenário: A TecProExpress quer modelar seus veículos de frota.

  1. Identifique as Entidades e Atributos para um veículo.
  2. Crie a tabela veiculo_frota usando DDL e insira uma tupla usando DML.

🚀 Script de Seed (Gabarito Físico)

-- DDL
CREATE TABLE veiculo_frota (
    id_veiculo INT PRIMARY KEY,
    placa VARCHAR(7) NOT NULL,
    capacidade_carga_kg DECIMAL(10,2)
);

-- DML
INSERT INTO veiculo_frota (id_veiculo, placa, capacidade_carga_kg) VALUES (101, 'ABC1234', 5000.00);


💻 Ponte Prática: Do SQL Manual ao SQLAlchemy 2.0 ORM

Como traduzir os conceitos matemáticos de Relação (Tabela), Tupla (Linha) e Domínio (Tipo de Dado) para o código Python moderno?

🔴 1. A Abordagem Manual (Dicionários em Memória sem Atomicidade)

No modelo procedural sem ORM, os dados de entidades vivem em dicionários desprotegidos. O desenvolvedor corre o risco de violar a atomicidade ou aceitar tipos inconsistentes:

# ❌ ABORDAGEM PROCEDURAL: Dicionários com tipos inconsistentes e campos soltos
fornecedores_memoria = [
    {"id": 1, "nome": "TecProExpress", "status": True},
    {"id": 2, "nome": 12345, "status": "ativo"} # ⚠️ Nome como número e status como string!
]

🟢 2. A Abordagem com SQLAlchemy 2.0 (Mapeamento Declarativo Tipado)

Com o SQLAlchemy 2.0, cada Relação é uma classe e cada Atributo possui seu Domínio explicitado com Type Hints:

# ✅ ABORDAGEM MODERNA: Mapeamento Conceitual -> Lógico -> Físico via SQLAlchemy
from sqlalchemy import create_engine, String, Boolean, Integer, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session

class Base(DeclarativeBase):
    pass

class FornecedorModel(Base):
    """Representa a Relação FORNECEDOR no Modelo Relacional."""
    __tablename__ = "fornecedores"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    nome: Mapped[str] = mapped_column(String(100), nullable=False)
    status_ativo: Mapped[bool | None] = mapped_column(Boolean, nullable=True) # Aceita NULL

    def __repr__(self) -> str:
        status_str = "Ativo" if self.status_ativo is True else ("Inativo" if self.status_ativo is False else "Pendente (NULL)")
        return f"Fornecedor(id={self.id}, nome='{self.nome}', status={status_str})"

🛠️ Mini-Projeto 05 (BD): Catálogo de Fornecedores e Mapeamento Relacional

Objetivo: Implementar o ciclo completo da modelagem conceitual à persistência física de fornecedores com manipulação de valores NULL.

📋 Pré-requisitos e Instalação

No terminal do seu ambiente virtual (PowerShell ou Bash), instale a biblioteca necessária:

pip install sqlalchemy

💻 Código Completo e Autocontido (miniprojeto_05_dominios.py)

Crie o arquivo miniprojeto_05_dominios.py e insira o código abaixo integralmente:

"""
Mini-Projeto 05: Catálogo de Fornecedores e Mapeamento Relacional
Curso: GTI - Banco de Dados Relacionais e Engenharia de Software
Stack: Python 3.11+ | SQLAlchemy 2.0 | SQLite
"""
import os
from sqlalchemy import create_engine, String, Integer, Boolean, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session

# 1. Definição Declarativa do Schema
class Base(DeclarativeBase):
    pass

class FornecedorModel(Base):
    __tablename__ = "fornecedores"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    nome: Mapped[str] = mapped_column(String(100), nullable=False)
    status_ativo: Mapped[bool | None] = mapped_column(Boolean, nullable=True)  # Aceita NULL

    def __repr__(self) -> str:
        status_str = "Ativo" if self.status_ativo is True else ("Inativo" if self.status_ativo is False else "Pendente (NULL)")
        return f"Fornecedor(id={self.id}, nome='{self.nome}', status={status_str})"

# 2. Ponto de Entrada Executável
if __name__ == "__main__":
    DB_FILE = "tecpro_fornecedores.db"

    # Reset preventivo para garantir idempotência em testes repetidos
    if os.path.exists(DB_FILE):
        os.remove(DB_FILE)

    engine = create_engine(f"sqlite:///{DB_FILE}", echo=False)
    Base.metadata.create_all(bind=engine)

    print("📐 MAPEAMENTO RELACIONAL: RELAÇÃO, TUPLAS E ATRIBUTOS")
    print("=" * 65)

    # 1. Inserindo tuplas relacionais
    with Session(engine) as session:
        f1 = FornecedorModel(nome="TecProExpress Matriz Logística", status_ativo=True)
        f2 = FornecedorModel(nome="Pneus & Cargas Brasil", status_ativo=False)
        f3 = FornecedorModel(nome="Novo Fornecedor em Homologação", status_ativo=None)  # NULL no banco

        session.add_all([f1, f2, f3])
        session.commit()
        print("✅ 3 Tuplas persistidas com sucesso na relação 'fornecedores'!")

    # 2. Consultando e exibindo as tuplas relacionais
    print("\n🔍 Consultando catálogo de fornecedores:")
    with Session(engine) as session:
        stmt = select(FornecedorModel)
        for fornecedor in session.scalars(stmt).all():
            print(f"  🏢 {fornecedor}")
    print("=" * 65)

🚀 Como Executar

Execute o script no terminal:

python miniprojeto_05_dominios.py

🖥️ Saída Esperada no Console

📐 MAPEAMENTO RELACIONAL: RELAÇÃO, TUPLAS E ATRIBUTOS
=================================================================
✅ 3 Tuplas persistidas com sucesso na relação 'fornecedores'!

🔍 Consultando catálogo de fornecedores:
  🏢 Fornecedor(id=1, nome='TecProExpress Matriz Logística', status=Ativo)
  🏢 Fornecedor(id=2, nome='Pneus & Cargas Brasil', status=Inativo)
  🏢 Fornecedor(id=3, nome='Novo Fornecedor em Homologação', status=Pendente (NULL))
=================================================================

💡 Checkpoint de Lógica

Importante

Reflexão Profissional: Por que a Modelagem Conceitual é considerada a fase mais crítica de um projeto de software? (Resposta: Porque código SQL mal feito pode ser reescrito rapidamente, mas se a equipe entender a regra de negócio errado no Modelo Conceitual, todo o sistema será construído para resolver o problema errado). 🧠🛡️



🧪 Quiz de Fixação e Autoavaliação — Capítulo 05

1. Quem foi o cientista da computação britânico que formulou o Modelo Relacional de Dados em 1970 nos laboratórios da IBM?

  • A) Alan Turing
  • B) Edgar Frank Codd (E. F. Codd)
  • C) Peter Chen
  • D) Linus Torvalds
💡 Ver Resposta e Justificativa

Resposta Correta: B
Justificativa: E. F. Codd revolucionou a computação em 1970 com o artigo histórico 'A Relational Model of Data for Large Shared Data Banks', introduzindo tabelas, tuplas e álgebra relacional.


2. Qual cardinalidade existe entre 'Cliente' e 'Pedido' em um sistema de e-commerce tradicional?

  • A) 1 : 1 (Um cliente só pode fazer um único pedido na vida).
  • B) 1 : N (Um cliente pode fazer muitos pedidos ao longo do tempo, mas cada pedido pertence a exatamente um cliente).
  • C) N : N (Um pedido pode pertencer a 50 clientes diferentes ao mesmo tempo).
  • D) Nenhuma relação.
💡 Ver Resposta e Justificativa

Resposta Correta: B
Justificativa: Relacionamento 1:N clássico: a Chave Primária do Cliente (id_cliente) viaja para a tabela de Pedidos como Chave Estrangeira (id_cliente_fk).


3. No Modelo Entidade-Relacionamento (MER), o que é um 'Atributo Multivalorado' (ex: Telefones de um Fornecedor)?

  • A) Um atributo que só aceita números negativos.
  • B) Um atributo que pode conter mais de um valor simultâneo para a mesma entidade (ex: um fornecedor com 3 números de telefone diferentes).
  • C) Um atributo que guarda o preço em dólares.
  • D) A chave primária da tabela.
💡 Ver Resposta e Justificativa

Resposta Correta: B
Justificativa: Atributos multivalorados violam a 1ª Forma Normal no modelo relacional e devem ser transformados em uma tabela filha separada no mapeamento lógico.


🎯 Laboratório Prático

Coloque este conhecimento em prática agora mesmo executando o roteiro autoguiado:
👉 ATIVIDADE 02: MODELAGEM CONCEITUAL