---
id: schema-design-tmpl
kind: template
agent: data-engineer
produces: docs/data-models/{dominio}-schema.md
---

# Schema Design: {nome do domínio/módulo}

**Status:** Draft
**Banco-alvo:** {PostgreSQL / Supabase / MySQL — versão}
**Fonte da arquitetura:** {docs/architecture/... — de onde vêm as entidades e o contexto}

## Contexto de Domínio

> O quadro completo antes do DDL. Sem isto, o schema vira palpite.

- **Entidades reais:** {as coisas do mundo do negócio que viram tabelas}
- **Relações principais:** {quem pertence a quem, cardinalidades}
- **Padrões de acesso:** {como os dados vão ser lidos e escritos — as queries quentes}
- **Escala esperada:** {volume por tabela, taxa de crescimento, hotpaths}
- **Restrições de segurança:** {multi-tenant? auth.uid()? dados sensíveis?}

## Modelo de Dados (visão lógica)

> Diagrama/lista de entidades e relações. Cada entidade rastreia a um requisito ou padrão de acesso documentado (No Invention).

| Entidade | Descrição | Relações | Fonte (FR/NFR/entidade) |
|---|---|---|---|
| {entidade} | {o que representa} | {FK → tabela} | {rastro} |

## Tabelas (DDL)

> Cada tabela com baseline obrigatório (`id`, `created_at`, `updated_at`), FKs explícitas e constraints que o banco faz cumprir. Soft delete (`deleted_at`) quando há trilha de auditoria.

### Tabela `{nome_tabela}`

**Propósito:** {por que esta tabela existe — o requisito que ela atende}

```sql
CREATE TABLE IF NOT EXISTS {schema}.{nome_tabela} (
  id          {uuid/bigint} PRIMARY KEY DEFAULT {gen_random_uuid()/...},
  {coluna}    {tipo} {NOT NULL/...} {DEFAULT ...},
  -- FKs explícitas
  {fk_coluna} {tipo} REFERENCES {schema}.{tabela_pai}(id) ON DELETE {RESTRICT/CASCADE/SET NULL},
  -- baseline
  created_at  timestamptz NOT NULL DEFAULT now(),
  updated_at  timestamptz NOT NULL DEFAULT now(),
  deleted_at  timestamptz,  -- soft delete, se aplicável
  -- constraints de integridade
  CONSTRAINT {nome_check} CHECK ({condição})
);
```

**Colunas:**

| Coluna | Tipo | Nullable | Default | Regra / Constraint |
|---|---|---|---|---|
| id | {tipo} | NO | {default} | PK |
| {coluna} | {tipo} | {SIM/NÃO} | {default} | {CHECK/UNIQUE/...} |

**Justificativa de normalização/desnormalização:** {por que normalizado ou onde desnormalizei e qual padrão de acesso justifica}

## Integridade Referencial

> Defesa em profundidade, no banco — não delegada à aplicação.

| FK | Tabela origem → destino | ON DELETE | Índice na FK? | Justificativa |
|---|---|---|---|---|
| {fk} | {origem} → {destino} | {ação} | {SIM/NÃO} | {por quê} |

## Constraints de Negócio

> Regras que o banco faz cumprir mesmo com bug na aplicação.

- **CHECK:** {constraint} — {regra de negócio que garante}
- **UNIQUE:** {coluna(s)} — {invariante que protege}
- **NOT NULL:** {colunas obrigatórias} — {por quê}
- **Triggers:** {nome} — {comportamento, ex.: `updated_at` automático}

## Estratégia de Índices

> Cada índice serve a uma query real de `## Contexto de Domínio`. Índice sem query que o justifique é peso morto.

| Índice | Tabela(s)/coluna(s) | Tipo | Query/padrão de acesso que justifica |
|---|---|---|---|
| {nome_idx} | {tabela(coluna)} | {btree/gin/...} | {query quente} |

## Segurança (RLS — visão de schema)

> Quais tabelas exigem Row Level Security. O detalhamento das políticas vai em `rls-policies-tmpl`.

| Tabela | RLS obrigatório? | Chave de tenancy | Observação |
|---|---|---|---|
| {tabela} | {SIM/NÃO} | {ex.: user_id / org_id} | {ex.: service_role bypassa} |

> Se não há camada de auth, registrar: `auth.uid()` retorna NULL — RLS não protege ainda.

## Riscos e Decisões Abertas

- {risco de modelagem, dívida registrada, decisão que depende do @architect}

## Change Log

| Data | Mudança | Autor |
|---|---|---|
| {data} | {o quê} | @data-engineer |
