# Modelo de Banco Multitenant

Data: 20/07/2026

## Mudanca de direcao

O projeto nao vai mais reaproveitar o banco legado como destino principal. Sera criado um banco novo para o motor NFS-e/ADN.

A estrutura continua preparada para conversar com sistemas externos/legados, mas os dados sincronizados ficam em uma base propria, com desenho multitenant por municipio.

## Objetivo do modelo

Evitar que consultas de um municipio concorram com o volume de outro municipio.

Em vez de uma tabela unica gigante:

```text
nfse_documentos
  municipio_id
  nsu
  ...
```

O modelo passa a usar tabelas fisicas por codigo IBGE:

```text
nfse_documentos_3550308
nfse_eventos_3550308
nfse_documento_partes_3550308

nfse_documentos_3304557
nfse_eventos_3304557
nfse_documento_partes_3304557
```

Assim, cada sistema/tenant municipal le suas proprias tabelas.

## Tabelas globais

As tabelas globais guardam apenas configuracao e controle:

- `nfse_municipios`;
- `nfse_certificados`;
- `nfse_sync_state`;
- `nfse_sync_logs`;
- `nfse_tenant_table_versions`.

Essas tabelas nao devem concentrar XML/documentos de todos os municipios.

## Tabelas por municipio

Para cada municipio habilitado, criar no minimo:

- `nfse_documentos_{codigo_ibge}`;
- `nfse_eventos_{codigo_ibge}`;
- `nfse_documento_partes_{codigo_ibge}`.

Exemplo para Sao Paulo:

```text
nfse_documentos_3550308
nfse_eventos_3550308
nfse_documento_partes_3550308
```

## Catalogo de tenants

A tabela `nfse_municipios` aponta para as tabelas fisicas usadas pelo tenant:

```text
codigo_ibge=3550308
tabela_documentos=nfse_documentos_3550308
tabela_eventos=nfse_eventos_3550308
tabela_partes=nfse_documento_partes_3550308
```

O worker usa esse catalogo para saber onde gravar. O sistema consumidor usa o mesmo catalogo, ou uma configuracao equivalente, para saber de qual tabela ler.

## Fluxo baseado no certificado

O consumo parte do certificado ativo vinculado ao municipio:

```text
nfse_municipios
  codigo_ibge=3550308

nfse_certificados
  municipio_id -> 3550308
  arquivo_path -> certificado A1 do municipio
```

Antes de consumir:

```text
1. worker busca municipios/certificados ativos;
2. para cada municipio, valida o codigo IBGE configurado;
3. verifica se as tabelas fisicas do codigo existem;
4. se existem, segue o consumo;
5. se nao existem, cria as tabelas pelo template;
6. abre o certificado e consulta o ADN;
7. grava na tabela do proprio municipio.
```

O certificado nao deve ser usado como unica fonte para descobrir o codigo IBGE, porque o `.pfx` normalmente identifica o titular por CNPJ/nome e nao garante um campo padrao com codigo IBGE. A fonte confiavel do codigo IBGE no motor e o cadastro `nfse_municipios`, com o certificado vinculado em `nfse_certificados`.

## Padrao de criacao

Para gerar SQL de um novo municipio:

```sh
./scripts/gerar_sql_tabelas_municipio.sh 3550308
```

Para garantir as tabelas no ambiente local com Docker:

```sh
./scripts/ensure_tabelas_municipio.sh 3550308
```

Esse script e idempotente: se as tres tabelas ja existem, ele apenas segue; se faltar alguma, aplica o template.

Para salvar em arquivo:

```sh
mkdir -p migrations/generated
./scripts/gerar_sql_tabelas_municipio.sh 3550308 > migrations/generated/create_tenant_3550308.sql
```

Depois aplicar no banco novo:

```sh
mysql -h HOST -u USER -p DATABASE < migrations/generated/create_tenant_3550308.sql
```

## Performance de leitura

Vantagens:

- indices menores por tabela;
- menos linhas por consulta;
- menos disputa entre tenants;
- manutencao por municipio mais simples;
- backups/arquivamentos podem ser planejados por tenant.

Indices definidos para o caminho quente em `nfse_documentos_{codigo_ibge}`:

```sql
UNIQUE KEY (ambiente, nsu)
UNIQUE KEY (chave_acesso)
KEY (data_emissao)
KEY (data_competencia)
KEY (numero_nfse)
KEY (prestador_documento)
KEY (tomador_documento)
KEY (status_documento, data_emissao)
KEY (prestador_documento, data_emissao)
KEY (tomador_documento, data_emissao)
KEY (data_competencia, numero_nfse)
KEY (codigo_tributacao_nacional, data_emissao)
```

Indices definidos para busca por partes em `nfse_documento_partes_{codigo_ibge}`:

```sql
UNIQUE KEY (documento_id, papel)
KEY (documento_federal)
KEY (papel, documento_federal)
KEY (municipio_codigo)
```

O XML bruto permanece salvo para auditoria e reprocessamento, mas listagens e filtros devem usar as colunas normalizadas.

Custos:

- mais tabelas no schema;
- migrations por tenant precisam ser geradas/aplicadas;
- consultas agregadas entre todos os municipios exigem `UNION ALL` ou processo analitico separado;
- o codigo precisa montar nomes de tabela com seguranca, sempre vindo do catalogo, nunca de input livre.

## Regra de seguranca para nomes de tabela

O codigo IBGE deve ser validado antes de gerar SQL:

```text
somente numeros
exatamente 7 digitos
```

O motor nao deve aceitar nome de tabela recebido diretamente de request/API. Ele deve buscar `tabela_documentos`, `tabela_eventos` e `tabela_partes` na tabela `nfse_municipios`.

## Consultas de exemplo

Buscar documentos de Sao Paulo por periodo:

```sql
SELECT id, nsu, chave_acesso, data_emissao, prestador_documento, tomador_documento, status_documento
FROM nfse_documentos_3550308
WHERE data_emissao >= '2026-07-01'
  AND data_emissao < '2026-08-01'
ORDER BY data_emissao DESC
LIMIT 100;
```

Buscar notas de um tomador:

```sql
SELECT id, nsu, chave_acesso, data_emissao, status_documento
FROM nfse_documentos_3550308
WHERE tomador_documento = '98765432000110'
  AND data_emissao >= '2026-07-01'
  AND data_emissao < '2026-08-01'
ORDER BY data_emissao DESC
LIMIT 100;
```

Buscar notas de um prestador:

```sql
SELECT id, nsu, numero_nfse, data_emissao, tomador_documento, valor_liquido
FROM nfse_documentos_3550308
WHERE prestador_documento = '12345678000190'
  AND data_emissao >= '2026-07-01'
  AND data_emissao < '2026-08-01'
ORDER BY data_emissao DESC
LIMIT 100;
```

Buscar partes normalizadas:

```sql
SELECT d.id, d.chave_acesso, p.papel, p.documento_federal, p.nome_razao
FROM nfse_documentos_3550308 d
JOIN nfse_documento_partes_3550308 p ON p.documento_id = d.id
WHERE p.documento_federal = '12345678000190';
```

## Quando criar mais tabelas por municipio

Se o consumo precisar de mais de uma tabela para performance ou organizacao, seguir o mesmo padrao:

```text
nome_da_tabela_{codigo_ibge}
```

Exemplos futuros:

- `nfse_servicos_3550308`;
- `nfse_impostos_3550308`;
- `nfse_anexos_3550308`;
- `nfse_erros_processamento_3550308`.

## Recomendacao atual

Manter globais apenas as tabelas de controle. Todo dado volumoso ou lido por sistema municipal deve ser fisicamente separado por codigo IBGE.
