Monalisa
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 com Z; 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ão now(): 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 em valid_until já pertence ao período seguinte. valid_until nulo 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.

ConstraintTabelaSem sobreposição para
broker_store_codes_no_overlapbroker_store_codes(tenant, corretor, IF): um código por corretor por IF de cada vez
membership_team_no_overlapmembership_teams(time, membership)
team_fi_service_no_overlapteam_financial_institution_accounts(time, vínculo CNPJ × IF)
team_hierarchy_edge_no_overlapteam_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

TabelaCHECK de períodoEXCLUDEUma aberta por vez (índice parcial)
broker_store_codessimsimnão
membership_teamssimsimsim
team_financial_institution_accountssimsimnão
team_hierarchiessimsimnão
membership_portfoliossimnãosim (por tenant e corretor)
bank_user_assignmentssimnãosim (por usuário bancário)
membership_access_profilesnãonãosim
membership_brokersnãonãosim
tenant_broker_accountsnãonãosim
broker_affiliations (started_on/ended_on)nãonãosim
company_ownerships (started_on/ended_on)nãonãonã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 das membership_teams de que dependem. Um CHECK olha uma linha só, então essa cobertura não é garantida pelo banco.
  • Ciclos na hierarquia. O EXCLUDE de team_hierarchies impede 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.

Nesta página