4.4 KiB
4.4 KiB
🗄️ Padrões de Banco de Dados (O Protocolo "Integridade Relacional")
Público: Agentes de Backend & Arquitetos. Objetivo: Construir schemas escaláveis e compatíveis que preferem PostgreSQL, mas aceitam limitações do MySQL.
[!CRITICAL] O Mandato dos Dados: "Código é temporário. Dados são permanentes. Schemas quebrados são uma sentença perpétua."
1. 🏗️ Arquitetura & Stack
A Camada de Abstração
- ORM Obrigatório: Use SQLAlchemy (Async) ou Prisma (se Node).
- Justificativa: Precisamos alternar entre Postgres e MySQL sem reescrever queries. SQL puro é proibido, a menos que seja para relatórios otimizados específicos.
- Migrações: Alembic (Python) ou Prisma Migrate.
- Regra: Nunca modifique o DB manualmente. Code-first sempre.
O Duelo: PostgreSQL vs MySQL
Preferimos PostgreSQL.
- Por que: JSONB, Melhor Indexação, Confiabilidade.
- Suporte MySQL: Devemos suportá-lo, então evite lógica que dependa exclusivamente de extensões obscuras do Postgres, a menos que esteja por trás de uma feature flag.
2. 🏛️ Regras de Design de Schema
Convenções de Nomenclatura (Snake_Case)
- Tabelas: Plural, snake_case (
users,order_items,audit_logs). - Colunas: Singular, snake_case (
created_at,user_id,is_active). - Chaves:
- Primária:
id(UUIDv7 ou BigInt otimizado). - Estrangeira:
target_id(ex:user_idreferenciandousers.id).
- Primária:
Disciplinas de Tipo
- Timestamps: SEMPRE use
UTC.- Coluna:
created_at(TIMESTAMP WITH TIME ZONE). - Coluna:
updated_at(Trigger de auto-update).
- Coluna:
- JSON: Use
JSONB(Postgres) /JSON(MySQL).- Restrição: Não trate o DB como um document store. Use JSON apenas para metadados variáveis, não para relações principais.
- Booleans: Use
BOOLEAN. (MySQL define como TinyInt(1) automaticamente, o ORM lida com isso).
3. 🛡️ Performance & Confiabilidade
Estratégia de Indexação
- Chaves Estrangeiras: DEVEM ser indexadas.
- Busca: Se buscar texto, use Trigram (Postgres) ou FullText (MySQL).
- Unicidade: Force no nível do DB (
unique=True), não apenas no nível do código.
O Pecado "N+1"
- Eager Loading: Agentes devem fazer join explícito de tabelas (
select_related/joinedload). - Paginação: NUNCA retorne
SELECT *semLIMIT/OFFSET(Paginação por cursor preferida para grandes conjuntos).
4. 🔒 Checklist de Compatibilidade (Postgres vs MySQL)
Antes de commitar uma migração, verifique:
- Quoting: Postgres usa aspas duplas
"tabela", MySQL usa crases`tabela`. Resultado: Use o ORM para lidar com isso. - Case Sensitivity: MySQL no Windows é case-insensitive. Postgres é case-sensitive. Resultado: Mantenha snake_case minúsculo explicitamente.
- Enums: ENUMs nativos são complicados em migrações. Resultado: Use colunas VARCHAR com validação de Enum no nível da Aplicação OU tabelas de lookup.
5. 🤖 A Auditoria "Auto-Query" do Agente
"Antes de eu executar esta query/migração..."
- Eu usei um arquivo de migração?
- O padrão de
created_atestá definido comonow()? - Estou buscando 10.000 linhas? (Adicione LIMIT).
- Se eu deletar um Pai, o que acontece com o Filho? (Defina
ON DELETE CASCADEouSET NULL).
6. ⏱️ Autodiagnóstico de Performance (A Checagem "Slow Query")
Agentes devem rodar estas checagens mentais ou reais em qualquer query complexa:
Teste A: O Ritual "Explain"
Antes de finalizar uma query, simule EXPLAIN (Postgres) ou EXPLAIN ANALYZE.
- Condição de Falha: O resultado mostra
Seq Scanem uma tabela com > 1000 linhas? - Correção: Adicione um index na coluna filtrada (
WHERE coluna = ...).
Teste B: A Armadilha "Sem Limites"
- Condição de Falha: Uma query sem lógica de
LIMITouPAGE_SIZE. - Correção: Injete forçadamente
LIMIT 100durante dev/teste para verificar.
Teste C: O Detector "N+1"
- Condição de Falha: Usar um loop para buscar dados relacionados.
users = session.query(User).all() for user in users: print(user.address) # 🚨 RUIM: Uma query por usuário - Correção: Use Eager Loading.
users = session.query(User).options(joinedload(User.address)).all() # ✅ BOM: Único JOIN