Garantias do banco
Integridade temporal
Vigências por dia em intervalos semiabertos, EXCLUDE contra sobreposição e horários gerados no banco.
Instantes e dias
- Todo instante é
timestamptz, armazenado em UTC. A convenção é serializar em ISO 8601 comZ; conversão de fuso é só apresentação. - Os horários de criação, consumo e auditoria são gerados pelo PostgreSQL, nunca aceitos do cliente. Os padrões das
colunas usam
clock_timestamp()(101 colunas no banco migrado), nãonow():now()é o início da transação e pode ser anterior a uma espera por lock, então não serve como corte depois dessa espera. - Vigência de negócio é
date, com intervalo semiaberto[valid_from, valid_until): o dia emvalid_untiljá pertence ao período seguinte.valid_untilnulo significa sem fim definido.
Os 4 EXCLUDE
Um EXCLUDE com daterange(valid_from, valid_until, '[)') e o operador && recusa duas linhas cujos períodos se
sobrepõem para a mesma chave. Como as chaves são bigint, os EXCLUDE usam GiST com a extensão btree_gist.
| Constraint | Tabela | Sem sobreposição para |
|---|---|---|
broker_store_codes_no_overlap | broker_store_codes | (tenant, corretor, IF): um código por corretor por IF de cada vez |
membership_team_no_overlap | membership_teams | (time, membership) |
team_fi_service_no_overlap | team_financial_institution_accounts | (time, vínculo CNPJ × IF) |
team_hierarchy_edge_no_overlap | team_hierarchies | (subordinado, superior): a mesma aresta |
ALTER TABLE broker_store_codes
ADD CONSTRAINT broker_store_codes_no_overlap
EXCLUDE USING gist (tenant_id WITH =, broker_id WITH =, financial_institution_id WITH =,
daterange(valid_from, valid_until, '[)') WITH &&);Essas quatro tabelas também têm um CHECK de período (valid_until IS NULL OR valid_until > valid_from).
Tabelas com vigência
| Tabela | CHECK de período | EXCLUDE | Uma aberta por vez (índice parcial) |
|---|---|---|---|
broker_store_codes | sim | sim | não |
membership_teams | sim | sim | sim |
team_financial_institution_accounts | sim | sim | não |
team_hierarchies | sim | sim | não |
membership_portfolios | sim | não | sim (por tenant e corretor) |
bank_user_assignments | sim | não | sim (por usuário bancário) |
membership_access_profiles | não | não | sim |
membership_brokers | não | não | sim |
tenant_broker_accounts | não | não | sim |
broker_affiliations (started_on/ended_on) | não | não | sim |
company_ownerships (started_on/ended_on) | não | não | não |
Onde a tabela não tem CHECK nem EXCLUDE, a ordem das datas e a ausência de sobreposição entre períodos já fechados ficam por conta da aplicação.
O que o banco não cobre
- Cobertura entre tabelas. Uma carteira (
membership_portfolios) e uma aresta de hierarquia (team_hierarchies) deveriam caber na vigência dasmembership_teamsde que dependem. Um CHECK olha uma linha só, então essa cobertura não é garantida pelo banco. - Ciclos na hierarquia. O EXCLUDE de
team_hierarchiesimpede a mesma aresta sobreposta no tempo, não ciclos nem inserções concorrentes que formem um. Isso exige uma constraint trigger ou um protocolo transacional, ainda não implementados. - Transferências. Fechar a origem e abrir o destino na mesma data (
portfolio_transfers,bank_user_transfers) numa única transação é regra da aplicação; nenhuma constraint liga as duas linhas.