Relacionamento Digitalcom o Cidadão
Parte V — Tecnologia
Parte V — TecnologiaCapítulo 34

Capítulo 34 — Banco de Dados e Persistência

Este capítulo detalha a estratégia de persistência e armazenamento de dados da Plataforma de Relacionamento Digital com o Cidadão, descrevendo como dados transacionais, documentais, analíticos e de auditoria são armazena…

34.1 Objetivo do Capítulo

Este capítulo detalha a estratégia de persistência e armazenamento de dados da Plataforma de Relacionamento Digital com o Cidadão, descrevendo como dados transacionais, documentais, analíticos e de auditoria são armazenados, segregados, consultados, replicados e preservados ao longo do ciclo de vida da plataforma.

A arquitetura de dados separa responsabilidades por tipo e natureza do dado: dados transacionais operacionais em bancos dos serviços de domínio, dados documentais no Document Service e armazenamento de objetos, dados de cache em Redis, dados de busca em índices especializados, dados vetoriais para IA em vector store, dados analíticos em Data Lake, e dados de auditoria em Audit Store imutável.

Nenhum componente único atende indistintamente a todas essas necessidades. A tecnologia é selecionada conforme a natureza do dado, o padrão de acesso, os requisitos de latência, consistência, isolamento e conformidade regulatória.


34.2 Papel da Persistência na Plataforma

A persistência é a camada que converte operações transientes em estado permanente. Uma falha nesta camada afeta toda a plataforma: perda de dados invalida a solicitação do cidadão, inconsistência entre bancos degrada a confiabilidade, vazamento de dados entre tenants viola conformidade, e indisponibilidade do armazenamento derruba os serviços.

A arquitetura de persistência é, portanto, uma das decisões arquiteturais mais críticas. Ela é documentada em detalhe, testada rigorosamente e monitorada continuamente.


34.3 Princípios da Arquitetura de Dados

A persistência da plataforma é governada por dez princípios fundamentais:

1. Ownership por Domínio — cada serviço de domínio é o único responsável por gravar em seu armazenamento. Nenhum outro serviço acessa diretamente as tabelas do domínio alheio. Colaboração ocorre por API síncrona ou por eventos assíncronos.

2. Isolamento Multi-Tenant Estrutural — dados de um tenant nunca são acessíveis por outro por erro de aplicação ou falha de controle de acesso. O isolamento é estrutural na tecnologia escolhida, não apenas por filtro de aplicação.

3. Menor Privilégio em Credenciais — cada serviço usa credenciais de banco específicas com permissões mínimas. Não existe credencial administrativa compartilhada entre serviços.

4. Consistência Definida pelo Caso de Uso — transações locais para operações dentro de um domínio; consistência eventual entre domínios via eventos com idempotência.

5. Cache como Otimização — Redis é usado para aceleração de consultas e coordenação, nunca como fonte implícita de verdade de dados de negócio.

6. Separação OLTP / OLAP — dados transacionais operacionais e dados analíticos têm armazenamentos e responsabilidades distintas. Um serviço nunca consulta o Data Lake para decisão de negócio em tempo real.

7. Minimização de Dados Pessoais — PII é armazenado apenas onde funcionalmente necessário, com finalidade declarada e política de retenção. Dados pessoais em cache, logs ou índices de busca são minimizados.

8. Rastreabilidade de Alterações — alterações sensíveis são registradas em trilha de auditoria imutável com identificação de quem alterou, quando, qual valor anterior e qual novo valor.

9. Retenção Governada — cada tipo de dado tem política de retenção e exclusão. Exclusão de dados pessoais é executada conforme LGPD: anonimização quando possível, exclusão permanente quando necessário.

10. Índices e Projeções Reconstruíveis — índices de busca, vector stores e caches são projeções reconstruíveis a partir dos dados de origem. Em caso de inconsistência, a origem prevalece e a projeção é refeita.


34.4 Tecnologia de Banco Operacional

34.4.1 PostgreSQL como SGBD Relacional

O banco relacional primário da plataforma é PostgreSQL na versão 14 ou superior. A escolha é justificada por: maturidade, confiabilidade em ambiente crítico, suporte a JSON/JSONB para dados semi-estruturados, recurso de particionamento, Row-Level Security, triggers para auditoria, LISTEN/NOTIFY para comunicação intra-banco, e comunidade robusta.

Cada serviço de domínio possui banco dedicado ou schema isolado no mesmo cluster, conforme a estratégia definida em ADR-111.

34.4.2 Propriedade por Serviço

Request Service      → banco request-service (ou schema request)
Workflow Service     → banco workflow-service (ou schema workflow)
Citizen Service      → banco citizen-service (ou schema citizen)
Document Service     → banco document-service (ou schema document)
Scheduling Service   → banco scheduling-service (ou schema scheduling)

Nenhum serviço acessa tabelas de outro serviço diretamente. Uma queries de Request Service que precisa de dados de Workflow Service consulta a Workflow API — não acessa o banco do workflow.

34.4.3 Credenciais e Permissões Mínimas

Cada serviço usa credenciais PostgreSQL específicas:

-- Usuário request-service-app
CREATE USER "request-service-app" WITH ENCRYPTED PASSWORD '...';
GRANT USAGE ON SCHEMA request TO "request-service-app";
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA request TO "request-service-app";
-- Sem permissão em schemas de outros serviços

Credenciais administrativas são rotacionadas regularmente e nunca compartilhadas entre equipes ou serviços.

34.4.4 Isolamento Multi-Tenant no Banco

A estratégia de isolamento multi-tenant no nível de banco é uma das decisões arquiteturais mais críticas. Três modelos são avaliados conforme o requisito:

Modelo 1: Banco Dedicado por Tenant

Cada tenant principal ou grande volume possui seu próprio banco PostgreSQL. Vantagens: isolamento máximo, backup e restauração por tenant, performance sem competição de recursos, conformidade contratual facilitada. Desvantagens: provisionamento, custo de infraestrutura, múltiplas migrações de schema, gestão operacional em escala.

Modelo 2: Schema por Tenant

Todos os tenants compartilham o mesmo cluster PostgreSQL, mas cada tenant tem seus próprios schemas. Vantagens: conformidade com GDPR (dados separados logicamente), gestão centralizada de backup, economia de recursos. Desvantagens: grande número de schemas (centenas), migrations muito lentas, conexões e memory overhead.

Modelo 3: Shared Schema com Tenant Discriminator

Todas as tabelas multi-tenant incluem coluna tenant_id como discriminator. Vantagens: simplicidade operacional extrema, economia de infraestrutura, escalabilidade horizontal ilimitada. Desvantagens: exige disciplina rigorosa no código (Tenant Context sempre aplicado), risco de data leakage por query sem filtro de tenant, impossível isolar backup por tenant.

Modelo Híbrida (Recomendado)

A maioria dos tenants em shared schema com tenant_id discriminator; tenants com requisitos especiais (grande volume, conformidade contratual, isolamento mandatório) em banco ou schema dedicado. O Tenant Context determina o roteamento automaticamente.

A decisão final é registrada como ADR-112 com justificativa técnica, financeira e de conformidade.

34.4.5 Padrões Obrigatórios em Shared Schema

Quando o modelo shared schema é adotado, os seguintes padrões são obrigatórios:

Tenant Discriminator em toda Tabela Multi-Tenant

CREATE TABLE requests (
  id              UUID PRIMARY KEY,
  tenant_id       VARCHAR NOT NULL,
  protocol_code   VARCHAR NOT NULL,
  subject_id      VARCHAR NOT NULL,
  status          VARCHAR NOT NULL,
  created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (tenant_id, subject_id) REFERENCES citizens(tenant_id, id)
);

Toda tabela multi-tenant inclui tenant_id como coluna NOT NULL. O tenant_id efetivo vem sempre do Tenant Context resolvido a partir da identidade autenticada — nunca de parâmetro enviado pelo cliente.

Constraints Multi-Tenant

-- Incorreto: dois tenants poderiam compartilhar o mesmo código
UNIQUE (protocol_code);

-- Correto: protocolo é único por tenant
UNIQUE (tenant_id, protocol_code);

-- Chave estrangeira deve incluir tenant para garantir referência dentro do tenant
FOREIGN KEY (tenant_id, citizen_id) REFERENCES citizens(tenant_id, id);

Sem constraint multi-tenant, dois tenants podem violar integridade referencial ou unicidade um do outro.

Índices Orientados a Queries Reais

-- Query comum: "listar solicitações do tenant de um cidadão, ordenadas por data"
CREATE INDEX idx_requests_tenant_citizen_created
ON requests (tenant_id, subject_id, created_at DESC);

-- Query de admin: "encontrar todas as solicitações de um tenant com status específico"
CREATE INDEX idx_requests_tenant_status
ON requests (tenant_id, status);

Índices incluem tenant_id como primeira coluna para garantir que a query use índice. Sem isso, queries podem escaneiar tabela inteira mesmo com filtro de tenant.

Row-Level Security (Camada Adicional)

Quando o banco PostgreSQL suporta, Row-Level Security é avaliado como camada adicional de proteção:

CREATE POLICY tenant_isolation ON requests
  USING (tenant_id = current_setting('app.current_tenant')::varchar);

ALTER TABLE requests ENABLE ROW LEVEL SECURITY;

RLS garante que mesmo uma query sem cláusula WHERE de tenant não retorna dados de outro tenant. Registrado como ADR-114 (adoção e custo de performance).


34.5 Transações e Consistência

34.5.1 Transações Locais ao Serviço

A plataforma não utiliza transações distribuídas (2PC — Two-Phase Commit). Transações permanecem locais ao serviço que gerencia o banco. Consistência entre domínios é obtida por eventos com Transactional Outbox e idempotência nos consumidores.

Fluxo Transacional Típico:

BEGIN TRANSACTION (banco do Request Service)
  1. UPDATE requests SET status = 'SUBMITTED', version = 2
     WHERE id = '...' AND tenant_id = '...' AND version = 1
  
  2. INSERT INTO outbox_event (
       aggregate_id, aggregate_type, event_type, payload, tenant_id, created_at
     ) VALUES (
       '...', 'REQUEST', 'REQUEST_SUBMITTED', '{...}', '...', NOW()
     )
COMMIT

Ambas operações são atômicas. Se falhar INSERT na outbox, toda a transação reverte. O RabbitMQ nunca recebe evento de uma transação que falhou.

Consumidor Recebe o Evento:

Workflow Service recebe REQUEST_SUBMITTED
  ├─ Verifica idempotência: existe workflow com idempotency_key?
  ├─ Sim → retorna resposta cached sem lado-a-lado
  └─ Não:
      BEGIN TRANSACTION (banco do Workflow Service)
        1. INSERT INTO workflows (request_id, ..., idempotency_key)
        2. INSERT INTO outbox_event (...)
      COMMIT

34.6 Controle de Concorrência

34.6.1 Optimistic Locking

A maioria das operações usa optimistic locking via coluna version:

UPDATE requests 
SET status = 'APPROVED', version = 19, updated_at = NOW()
WHERE id = '...' AND tenant_id = '...' AND version = 18;

Se nenhuma linha for afetada (version != 18), a aplicação recebe zero rows affected e retorna 409 Conflict ao cliente com instrução de recarregar e tentar novamente.

Optimistic locking não bloqueia leituras, permitindo altíssima concorrência para casos sem conflito real.

34.6.2 Pessimistic Locking

Pessimistic locking é utilizado para seções críticas curtas com recurso escasso (ex.: última vaga em agendamento). Exemplo:

BEGIN TRANSACTION;
SELECT * FROM scheduling_slots 
WHERE tenant_id = '...' AND date = '...' 
FOR UPDATE SKIP LOCKED;
-- Uma única conexão obtém o lock
UPDATE scheduling_slots SET booked_count = booked_count + 1 
WHERE id = '...' AND booked_count < total_count;
COMMIT;

Pessimistic locking é evitado em operações longas, pois bloqueia outros processos por período estendido.

34.6.3 Distributed Locks via Redis

Para coordenação entre múltiplas instâncias de um serviço (ex.: apenas uma instância executa reconciliação periódica por tenant), distributed locks via Redis são utilizados:

const lockKey = `lock:reconciliation:${tenantId}`;
const lockValue = uuid(); // owner token único
const lockTTL = 300; // 5 minutos

// Adquirir lock atomicamente com timeout
const acquired = await redis.set(
  lockKey, 
  lockValue, 
  'EX', lockTTL, 
  'NX'  // Apenas se não existe
);

if (acquired) {
  try {
    await reconcile(tenantId);
  } finally {
    // Liberar somente se o token ainda pertence a nós
    await redis.eval(
      'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end',
      1, lockKey, lockValue
    );
  }
}

34.7 Migrações de Schema

34.7.1 Versionamento Automatizado

Migrações de schema são automatizadas com Flyway ou Liquibase (decisão padronizada em ADR-130). Cada migração é um arquivo versionado no repositório de código do serviço:

src/main/resources/db/migration/
  V001__create_requests_table.sql
  V002__add_tenant_id_column.sql
  V003__create_idx_requests_tenant_status.sql

Migrações são executadas automaticamente no startup do serviço. Rollback é evitado; migrações são apenas forward.

34.7.2 Estratégia Expand-Migrate-Contract para Zero-Downtime

Para alterações de schema que exigem zero-downtime (nunca derrubar o serviço), a estratégia Expand-Migrate-Contract é aplicada:

1. EXPAND (Release N) Adicionar nova coluna nullable ou nova tabela sem afetar código existente:

ALTER TABLE requests ADD COLUMN new_status VARCHAR;

2. DEPLOY Versão N+1 e N do serviço rodam simultaneamente. Código N continua usando coluna antiga; código N+1 começa a popular coluna nova durante operações de escrita.

3. MIGRATE Backfill de dados existentes (em batches se grande volume):

UPDATE requests SET new_status = status_mapping[status] 
WHERE new_status IS NULL;

Backfills longos nunca são bloqueantes no startup. São jobs assíncronos com checkpoint, rate control e observabilidade.

4. SWITCH Código da aplicação migra para usar apenas coluna nova. Coluna antiga não é mais escrita.

5. CONTRACT (Release N+2) Remover coluna antiga em release posterior, quando todas instâncias da versão N foram descomissionadas.


34.8 Temporalidade e Timestamps

A plataforma distingue semanticamente quatro tipos de timestamp:

CampoSignificadoExemplo
created_atQuando o registro foi persistido no bancoRequisição INSERT em 2026-07-16 10:30:00 UTC
updated_atQuando o registro foi modificado por últimoÚltima UPDATE em 2026-07-16 14:45:00 UTC
occurred_atQuando o fato de negócio efetivamente ocorreuSolicitação aberta pelo cidadão em 2026-07-16 10:25:00 UTC
effective_from / effective_toVigência de uma regra, configuração ou versãoPolítica de retenção vigente de 2026-01-01 a 2026-12-31

Timezone Obrigatório:

Todos os timestamps são armazenados em UTC (UTC+0). A interface apresenta o timestamp no fuso horário do usuário ou do tenant. Conversão ocorre na camada de apresentação, nunca no banco.

-- Correto: armazena em UTC
INSERT INTO requests (created_at) VALUES (CURRENT_TIMESTAMP AT TIME ZONE 'UTC');

-- Busca: 
SELECT created_at AT TIME ZONE 'America/Sao_Paulo' FROM requests;

34.9 Trilha de Auditoria

34.9.1 Componentes da Trilha

Alterações sensíveis são registradas em trilha imutável com: data/hora UTC, tenant_id, subject_id (usuário que executou), tipo de ação, recurso afetado, valor anterior, valor novo e resultado.

O Audit Store é segregado do banco operacional. Em outras palavras, mesmo que o banco de domínio seja comprometido, a trilha de auditoria permanece íntegra.

34.9.2 Implementação com Triggers

A trilha é registrada automaticamente via triggers de banco:

-- Tabela de auditoria no schema separado
CREATE TABLE audit.audit_log (
  id           BIGSERIAL PRIMARY KEY,
  tenant_id    VARCHAR NOT NULL,
  table_name   VARCHAR NOT NULL,
  row_id       VARCHAR NOT NULL,
  action       VARCHAR NOT NULL, -- INSERT, UPDATE, DELETE
  subject_id   VARCHAR NOT NULL,
  occurred_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  old_values   JSONB,
  new_values   JSONB,
  correlation_id VARCHAR
);

-- Trigger que registra UPDATE na tabela requests
CREATE OR REPLACE FUNCTION audit.requests_update() 
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO audit.audit_log (
    tenant_id, table_name, row_id, action, subject_id,
    old_values, new_values
  ) VALUES (
    NEW.tenant_id, 'requests', NEW.id, 'UPDATE', 
    current_setting('app.current_subject_id', true),
    to_jsonb(OLD), to_jsonb(NEW)
  );
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_audit_requests_update
AFTER UPDATE ON requests
FOR EACH ROW
EXECUTE FUNCTION audit.requests_update();

34.9.3 Dados Pessoais na Trilha

Dados pessoais na trilha são minimizados. A trilha registra referências (IDs e tipos de alteração), não cópias completas de dados sensíveis. Se necessário referenciar nome do cidadão, apenas o ID é incluído.


34.10 Retenção e Exclusão de Dados

34.10.1 Política de Retenção por Domínio

Cada tipo de dado tem política de retenção definida conforme requisito de negócio e LGPD:

Tipo de DadoRetençãoJustificativa
Solicitações finalizadas5 anos após conclusãoPrazo de auditoria administrativa
Logs de auditoria5 anosConformidade e investigação
Dados de cidadãoEnquanto vínculo ativo + 6 mesesLGPD art. 16 — eliminação após finalidade
Sessões ativasTTL da sessão (15 min)Segurança
Logs de aplicação90 diasOperacional
Documentos em GED5 anos após upload (configurável por tenant)Documentação de processos
Telemetria operacional90 diasDiagnóstico

34.10.2 Exclusão Lógica vs. Física

A plataforma adota exclusão lógica por padrão (deleted_at timestamp):

UPDATE documents 
SET deleted_at = NOW() 
WHERE id = '...' AND tenant_id = '...';

A exclusão lógica preserva a possibilidade de auditoria e recuperação, e é compatível com LGPD quando combinada com restrição de leitura ao contexto administrativo (apenas administrador do tenant vê soft-deleted).

34.10.3 Anonimização Conforme LGPD

Para cumprir direito ao esquecimento (LGPD art. 18, VI), a plataforma anonimiza dados pessoais identificáveis mantendo o registro técnico:

UPDATE citizens
SET 
  name = 'CIDADÃO ANONIMIZADO',
  email = CONCAT('anonimo-', id, '@example.com'),
  phone = NULL,
  birth_date = NULL,
  anonymized_at = NOW()
WHERE id = '...' AND tenant_id = '...';

O ID técnico (UUID) é preservado para permitir rastreabilidade de ações em auditoria sem expor dados pessoais.

34.10.4 Hard Delete para Conformidade Rigorosa

Em situações extremas (cumprimento de ordem judicial, vazamento confirmado de dados), hard delete é executado em job controlado com trilha de auditoria da própria exclusão:

DELETE FROM documents WHERE id = '...' AND tenant_id = '...';
DELETE FROM audit_hard_delete_log (...);

34.11 Conexões e Performance

34.11.1 Pool de Conexões

Cada serviço usa pool de conexões com limite dimensionado conforme carga:

spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 5000
      idle-timeout: 600000
      max-lifetime: 1800000

Pool maior aumenta concorrência, mas cada conexão consome memória do servidor PostgreSQL (tipicamente 5-10 MB por conexão). O sizing correto depende do workload medido.

34.11.2 Query Optimization

Queries são otimizadas continuamente com EXPLAIN ANALYZE, índices apropriados, e monitoramento de queries lentas (slow query log):

-- Ativar log de queries lentas (> 1 segundo)
ALTER SYSTEM SET log_min_duration_statement = 1000;
SELECT pg_reload_conf();

34.11.3 Connection Limit Monitoring

Limite de conexões ao PostgreSQL é monitorado e alerta quando exceder threshold. Um número excessivo de conexões indica vazamento de conexões na aplicação (connections abertas e não fechadas).


34.12 Backup e Recuperação

34.12.1 Estratégia de Backup

A estratégia de backup combina três técnicas:

Backup Físico (pg_basebackup):

Executado diariamente em horário de menor carga. Backup completo da instância PostgreSQL. Retenção: 30 dias. Armazenado em storage criptografado segregado do ambiente de produção.

Backup Lógico (pg_dump):

Executado a cada 6 horas. Exporta schema + dados em formato texto. Retenção: 7 dias. Usado para restore seletivo (apenas um banco ou schema) e migração entre versões.

Point-in-Time Recovery (PITR):

WAL archiving continuamente habilitado. Permite restaurar o banco em qualquer timestamp dentro da janela de retenção. Retenção padrão: 7 dias. Janela configurável conforme criticidade do serviço.

34.12.2 Restore de Banco

Restore é testado periodicamente (trimestralmente) em ambiente de staging:

# Restore completo
pg_restore -d cidadao_staging /backup/cidadao_2026-07-16.dump

# Restore até timestamp específico
pg_basebackup + WAL replay com --target-time '2026-07-16 14:30:00'

34.12.3 Restore por Tenant

Quando o modelo é shared schema com tenant discriminator, restore por tenant é impossível via backup de banco. Solução:

  • Export por tenant: pg_dump com filtro WHERE tenant_id = X; restore apenas de dados do tenant;
  • Restore completo + masking: restaura banco completo em ambiente isolado; administradores do tenant podem visualizar apenas dados do próprio tenant.

A estratégia de recuperação por tenant é registrada como ADR-124.


34.13 Alta Disponibilidade e Replicação

34.13.1 Replicação Streaming

PostgreSQL é configurado com replicação streaming para réplica primária + 2 réplicas assíncronas (ou síncronas conforme criticidade):

Aplicação
  │
  ├─ INSERT/UPDATE/DELETE → Primary
  │
Primary
  │
  ├─ WAL streaming → Replica 1 (sync) — failover
  ├─ WAL streaming → Replica 2 (async)
  └─ WAL streaming → Replica 3 (async)

Síncrono garante que a transação não é confirmada até que pelo menos uma réplica tenha recebido o WAL — garante zero perda de dados em failover. Assíncrono tem latência menor mas risco de perda de dados em failover.

34.13.2 Failover Automático

Em produção, Patroni + etcd ou pg_auto_failover gerenciam failover automático. Quando a primária torna-se indisponível, uma réplica é promovida em segundos. Aplicações detectam e reconectam-se à nova primária.

34.13.3 Read Replicas para Queries Analíticas

Read replicas segregam carga analítica (dashboards, relatórios) da transacional. Aplicações de analytics apontam para read replica; produção aponta para primária. Latência de replicação (tipicamente <1 segundo) é tolerável para analytics.


34.14 Observabilidade do Banco

34.14.1 Métricas PostgreSQL

Métricas coletadas e expostas via Prometheus:

  • conexões ativas vs. máximo permitido;
  • queries por segundo;
  • latência média de query;
  • replicação lag;
  • cache hit ratio;
  • index hit rate;
  • locks ativos.

34.14.2 Alertas Configurados

Alertas críticos:

  • conexão ao banco falhou por mais de 30 segundos;
  • replicação lag > 60 segundos;
  • número de conexões ativas > 80% do máximo;
  • queries com latência > 5 segundos em alta frequência;
  • disk usage > 85% do tamanho provisionado.

34.14.3 Tracing de Queries

Queries são correlacionadas com traces distribuídos via OpenTelemetry. Cada query tem span com nome descritivo, identificador de tenant, e tempo de execução. Queries lentas (>1 segundo) são automaticamente destacadas.


34.15 Banco Vetorial e Embeddings

34.15.1 Vector Store para RAG

O vector store armazena embeddings de documentos e conteúdo autorizado para suporte ao RAG (Retrieval-Augmented Generation) dos assistentes de IA:

Documento autorizado
    │
    ├─ Sanitização e chunking (blocos de até 500 tokens)
    │
    ├─ Geração de embedding via LLM Gateway
    │
    ├─ Armazenamento no vector store do tenant
    │
    └─ Publicação de evento ai.knowledge-indexed.v1

34.15.2 Isolamento por Tenant

Cada tenant tem sua própria coleção ou namespace vetorial. Uma query de busca semântica do tenant A nunca retorna conteúdo do tenant B — segregação é estrutural, não apenas por filtro de aplicação.

34.15.3 Reconstruibilidade

O vector store é reconstruível a partir dos documentos autorizados armazenados no Document Service / GED. Em caso de inconsistência, a origem (Document Service) prevalece e a projeção vetorial é refeita via job de reindexação.



34.16 Rastreabilidade com Anexo III

Item ANX-IIIAtendimento
6.2Controle de acesso com isolamento multi-tenant no nível de banco (tenant_id em constraints e índices)
6.3Auditoria de alterações via triggers e trilha imutável em schema segregado
1.1Integração de dados entre módulos via eventos com idempotência e Transactional Outbox

34.17 Benefícios da Arquitetura de Dados

  • isolamento multi-tenant garantido estruturalmente — tenant_id em constraints, não apenas em filtros de aplicação;
  • consistência eventual entre domínios — sem deadlocks de transações distribuídas; eventos com Transactional Outbox;
  • auditoria completa — trilha imutável segregada com timestamps UTC e subject_id;
  • escalabilidade horizontal — bancos segregados por serviço; replicação e particionamento por tenant;
  • recuperabilidade — backup físico, lógico e PITR; restore testado trimestralmente;
  • performance otimizada — índices multi-tenant, cache com invalidação ativa, read replicas para analytics;
  • conformidade LGPD — retenção governada, anonimização e exclusão controlada, minimização de PII;
  • observabilidade completa — métricas, alertas e tracing de queries com identificador de tenant.

34.18 Riscos e Mitigações

RiscoConsequênciaMitigação
Tensor sem tenant_id em filtroVazamento de dados entre tenantsConstraint de unicidade inclui tenant_id; índices incluem tenant_id; re-validação em aplicação
N+1 queriesLatência, sobrecarga do bancoQueries otimizadas com JOINs; eager loading; cache de dados referência
Cache inconsistenteDado obsoleto entregue como atualInvalidação ativa por evento; TTL explícito; fallback à origem
Backup não testadoImpossibilidade de recover em incidenteRestore testado trimestralmente em staging; documentação de RTO/RPO
Conexão não devolvida ao poolEsaurimento de conexões; bloqueio de aplicaçãoPool configurado; monitoring de conexões ativas; timeout de conexão
Replicação lag não monitoradoDados inconsistentes lidos de réplicaLag monitorado e alerta > 60 segundos; queries críticas leem de primária
Auditoria correlacionada ao banco operacionalComprometimento da auditoriaSchema segregado; triggers em transação separada; retenção longa
Exclusão de dados sem conformidade LGPDViolação de privacidadeAnonimização padrão; exclusão física em casos extremos com trilha de auditoria da exclusão

34.19 Decisões Arquiteturais

ADRTema
ADR-111Isolamento multi-tenant — banco por serviço, schema por tenant ou shared schema com discriminator
ADR-112Estratégia de segregação multi-tenant no nível de banco (constraints, índices, RLS)
ADR-113Tecnologia de banco para cada domínio (PostgreSQL, especializado)
ADR-114Row-Level Security (RLS) — adoção e limitações
ADR-115Transações — local vs. distribuída; Transactional Outbox pattern
ADR-116Replicação PostgreSQL — síncrona vs. assíncrona
ADR-117Backup — frequência, retenção, PITR; restore automático vs. manual
ADR-118Cache Redis — TTL, invalidação, política de falha
ADR-119Vector store para RAG — tecnologia e segregação por tenant
ADR-120Migrations de schema — Flyway/Liquibase; expand-migrate-contract
ADR-121Temporalidade — campos created_at, updated_at, occurred_at em UTC
ADR-122Auditoria — triggers vs. aspectos; trilha segregada vs. banco operacional
ADR-123Retenção e exclusão — soft delete vs. hard delete; anonimização LGPD
ADR-124Restore por tenant — viabilidade e estratégia em shared schema

34.20 Considerações Finais

A arquitetura de dados é a fundação da plataforma. A segregação correcta de multi-tenant no nível de banco — com tenant_id em constraints, índices e RLS quando aplicável — não é um detalhe operacional. É a diferença entre um sistema seguro e um sistema vulnerável a vazamento de dados.

A escolha entre banco por tenant, schema por tenant ou shared schema é trade-off entre isolamento máximo (banco/schema), operação em escala (shared schema) e custo. Qualquer que seja a escolha, Tenant Context derivado de identidade validada deve permear toda a arquitetura.

O padrão de Transactional Outbox garante que eventos são gravados na mesma transação dos dados operacionais, eliminando a possibilidade de perda de evento ou gravação de dado sem publicação. A consistência eventual entre domínios é alcançada sem deadlocks de transações distribuídas.

A auditoria segregada do banco operacional preserva integridade mesmo em cenários de comprometimento. Dados pessoais na auditoria são minimizados. Conformidade com LGPD é estruturada em retenção, anonimização e exclusão.

O backup em múltiplas formas (físico, lógico, PITR) garante recuperabilidade em cenários diversos. Restore testado trimestralmente valida que a estratégia realmente funciona — não apenas em teoria.

O Capítulo 35 detalha Cache Distribuído.


34.21 Controle de Versão

CampoValor
DocumentoDocumento Mestre — Plataforma de Relacionamento Digital com o Cidadão
Capítulo34 — Banco de Dados e Persistência
Versão1.0
SituaçãoConcluído
Última atualização16/07/2026

34.22 Rastreabilidade PRODEMGE

  • [ANX-III] Bloco 1 — itens 1.1, 1.2 (dados compartilhados entre serviços via eventos; cada serviço com banco próprio)
  • [ANX-III] Bloco 6 — itens 6.2, 6.3 (controle de acesso multi-tenant; auditoria de alterações)
  • [ANX-IV] Itens 3.1.6, 3.1.7, 3.1.8 — escalabilidade por replicação e particionamento; isolamento estrutural; consistência via Transactional Outbox
  • [ANX-V] Item 2.1 — migrations versionadas com Flyway; estratégia expand-migrate-contract para zero-downtime
  • [ANX-V] Item 2.3 — retenção governada, anonimização LGPD, direito ao esquecimento, exclusão controlada
  • [ANX-V] Item 2.4 — backup físico + lógico + PITR; restore testado trimestralmente; RTO/RPO definidos por serviço
  • [PNR] — Banco de dados PostgreSQL relacional como tecnologia principal; cache Redis; vetor store segregado por tenant; data lake por tenant
  • [EDITAL] — Edital CP001/2026: isolamento multi-tenant estrutural; LGPD compliant; backup, recuperação e replicação

34.23 Padrões de Conformidade Multi-Tenant no Banco

-- Padrão 1: Toda tabela multi-tenant inclui tenant_id NOT NULL
CREATE TABLE <entity> (
  id         UUID PRIMARY KEY,
  tenant_id  VARCHAR NOT NULL,
  ...
  FOREIGN KEY (tenant_id) REFERENCES tenants(id)
);

-- Padrão 2: Toda constraint de unicidade é (tenant_id, chave natural)
ALTER TABLE <entity> ADD CONSTRAINT uniq_<entity>_natural_key
  UNIQUE (tenant_id, natural_key_column);

-- Padrão 3: Toda foreign key cross-table inclui tenant_id
ALTER TABLE <entity> ADD CONSTRAINT fk_<entity>_<other>_<fk>
  FOREIGN KEY (tenant_id, <fk_column>) REFERENCES <other_table>(tenant_id, id);

-- Padrão 4: Todo índice multi-tenant começa com tenant_id
CREATE INDEX idx_<entity>_<access_pattern>
  ON <entity> (tenant_id, <colunas_do_padrão_de_acesso>);

-- Padrão 5: Trigger de auditoria registra tenant_id automaticamente
CREATE TRIGGER trg_audit_<entity>_<action>
AFTER <action> ON <entity>
FOR EACH ROW
EXECUTE FUNCTION audit.<entity>_<action>();

Estes cinco padrões garantem que o isolamento multi-tenant é estrutural no banco de dados. Uma violação acidental é detectada via constraint ou trigger antes de retornar dados ao cliente.


34.24 Operação de Banco — Runbook

34.24.1 Monitoramento Diário

MétricaThresholdAção
Conexões ativas< 80% do máximoAlerta em > 80%; intervenção em > 90%
Replicação lag< 60sAlerta em > 60s; escalação em > 5min
Slow queries< 10 queries/hora > 5sRevisão diária das queries mais lentas
Disk usage< 85% provisionadoExpansão de storage se > 80%
Backup diárioExecutado com sucessoAlerta se falhar; backup manual de emergência

34.24.2 Rotação de Credenciais

Credenciais de banco são rotacionadas trimestralmente. Procedimento:

  1. Gerar nova credencial forte;
  2. Aplicar em ambiente de staging; testar por 1 semana;
  3. Aplicar em produção em janela de baixa carga;
  4. Atualizar secrets no cofre (Vault/SOPS);
  5. Reiniciar serviços para aplicar nova credencial;
  6. Revogar credencial antiga após 24h de comprovação de sucesso.

34.24.3 Atualizações de Versão PostgreSQL

Atualizações de versão (ex.: PostgreSQL 15 → 16) seguem procedimento controlado:

  1. Atualizar versão em staging; executar suite de testes de regressão;
  2. Atualizar em produção com janela de manutenção anunciada;
  3. Migrar réplicas uma por uma (rolling upgrade);
  4. Validar integridade de dados (constraints, contagens, testes E2E);
  5. Monitorar métricas por 1 semana pós-upgrade.

Fim do Capítulo 34 — Banco de Dados e Persistência

Nesta página

34.1 Objetivo do Capítulo34.2 Papel da Persistência na Plataforma34.3 Princípios da Arquitetura de Dados34.4 Tecnologia de Banco Operacional34.4.1 PostgreSQL como SGBD Relacional34.4.2 Propriedade por Serviço34.4.3 Credenciais e Permissões Mínimas34.4.4 Isolamento Multi-Tenant no Banco34.4.5 Padrões Obrigatórios em Shared Schema34.5 Transações e Consistência34.5.1 Transações Locais ao Serviço34.6 Controle de Concorrência34.6.1 Optimistic Locking34.6.2 Pessimistic Locking34.6.3 Distributed Locks via Redis34.7 Migrações de Schema34.7.1 Versionamento Automatizado34.7.2 Estratégia Expand-Migrate-Contract para Zero-Downtime34.8 Temporalidade e Timestamps34.9 Trilha de Auditoria34.9.1 Componentes da Trilha34.9.2 Implementação com Triggers34.9.3 Dados Pessoais na Trilha34.10 Retenção e Exclusão de Dados34.10.1 Política de Retenção por Domínio34.10.2 Exclusão Lógica vs. Física34.10.3 Anonimização Conforme LGPD34.10.4 Hard Delete para Conformidade Rigorosa34.11 Conexões e Performance34.11.1 Pool de Conexões34.11.2 Query Optimization34.11.3 Connection Limit Monitoring34.12 Backup e Recuperação34.12.1 Estratégia de Backup34.12.2 Restore de Banco34.12.3 Restore por Tenant34.13 Alta Disponibilidade e Replicação34.13.1 Replicação Streaming34.13.2 Failover Automático34.13.3 Read Replicas para Queries Analíticas34.14 Observabilidade do Banco34.14.1 Métricas PostgreSQL34.14.2 Alertas Configurados34.14.3 Tracing de Queries34.15 Banco Vetorial e Embeddings34.15.1 Vector Store para RAG34.15.2 Isolamento por Tenant34.15.3 Reconstruibilidade34.16 Rastreabilidade com Anexo III34.17 Benefícios da Arquitetura de Dados34.18 Riscos e Mitigações34.19 Decisões Arquiteturais34.20 Considerações Finais34.21 Controle de Versão34.22 Rastreabilidade PRODEMGE34.23 Padrões de Conformidade Multi-Tenant no Banco34.24 Operação de Banco — Runbook34.24.1 Monitoramento Diário34.24.2 Rotação de Credenciais34.24.3 Atualizações de Versão PostgreSQL