Arquitetura de Dados
Esta pagina documenta todas as decisoes arquiteturais, mecanismos de integridade, performance e robustez implementados na camada de dados da plataforma ProCases.
Visao Geral
| Componente | Tecnologia | Versao |
|---|---|---|
| Banco Principal | PostgreSQL | 15+ (Alpine) |
| ORM | Prisma | 5.10.2 |
| Cache / Locks / Pub/Sub | Redis | 7+ |
| IDs | Snowflake (customizado) | 64-bit BigInt |
| Valores Monetarios | BIGINT centavos | Zero imprecisao |
Decisoes Arquiteturais
Por que PostgreSQL Nativo (sem TimescaleDB)
A plataforma utiliza PostgreSQL 15 padrao com extensoes nativas ao inves de TimescaleDB. Motivos:
| Criterio | PostgreSQL Nativo | TimescaleDB |
|---|---|---|
| Complexidade operacional | Baixa | Media-Alta (extensao + versao compativel) |
| Compatibilidade Prisma | Total | Parcial (hypertables nao suportadas pelo Prisma) |
| Particionamento | Nativo (PARTITION BY RANGE) | Automatico (hypertables) |
| Caso de uso ideal | Transacional + analitico misto | Series temporais puras (IoT, metricas/s) |
| Overhead | Zero | ~5-10% em writes normais |
| Busca fuzzy | pg_trgm + GIN | pg_trgm + GIN (igual) |
| Custo | Zero | Licenca Enterprise para features avancadas |
Conclusao: Para uma plataforma de gambling com carga transacional mista (financeiro + gaming + analytics), PostgreSQL nativo com partial indexes e particionamento manual oferece controle total sem dependencias extras.
Por que PostgreSQL Full-Text Search (sem Elasticsearch)
A extensao pg_trgm com index GIN resolve busca fuzzy por nome de item sem necessidade de um servico externo:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX idx_items_name_trgm ON items USING gin (name gin_trgm_ops);- Busca fuzzy:
WHERE name % 'dragon'encontra "Dragon Lore", "Dragonfire" - Busca parcial:
WHERE name ILIKE '%drag%'com performance O(1) via GIN - Zero infraestrutura extra: Sem Elasticsearch, sem sincronizacao de dados
- Consistencia garantida: Dados sempre atualizados (mesmo banco)
Por que BIGINT para Valores Monetarios (sem Decimal/Float)
Todos os modelos com campos monetarios utilizam BigInt. Nenhum campo usa Decimal, Float ou String para dinheiro.
R$ 1.863,21 = 186321 centavos (BigInt)Campos monetarios implementados (todos BigInt):
| Tabela | Campos |
|---|---|
| User | balanceCents, totalDepositedCents, totalWithdrawnCents, totalWageredCents |
| Transaction | amountCents |
| Deposit | amountCents |
| Withdrawal | amountCents |
| Item | valueCents (USD), valueCentsBrl (BRL), originalPriceCents |
| CaseOpening | pricePaidCents, itemValueCents, profitCents |
| Case | priceCents |
| Upgrade | valueInvestedCents, balanceUsedCents, targetValueCents |
| Battle | totalValueCents |
| BattlePlayer | totalProfitCents |
| BattleSettlement | valueCents |
| RafflePrize | valueCents, valueCentsBrl |
Beneficios:
- Zero erro de ponto flutuante (
0.1 + 0.2 == 0.3sempre) - Operacoes atomicas no banco (incremento/decremento sem race condition)
- Comparacoes exatas (sem tolerancia de epsilon)
- Compativel com JavaScript BigInt nativo
Por que Snowflake IDs (sem UUID / Auto-Increment)
Todos os IDs sao Snowflake de 64 bits gerados localmente:
[41 bits timestamp][10 bits machine ID][12 bits sequence]| Parametro | Valor |
|---|---|
| Epoch | 1 de Janeiro de 2024 (1704067200000) |
| Timestamp | 41 bits (~69 anos ate overflow) |
| Machine ID | 10 bits (1024 maquinas) |
| Sequence | 12 bits (4096 IDs/ms/maquina) |
| Tamanho | 8 bytes (BigInt) |
Comparacao:
| Criterio | Snowflake | UUID v4 | Auto-Increment |
|---|---|---|---|
| Tamanho | 8 bytes | 16 bytes | 4-8 bytes |
| Ordenavel por tempo | Sim | Nao | Sim |
| Sem coordenacao | Sim | Sim | Nao (requer banco) |
| Seguranca (nao previsivel) | Media | Alta | Baixa |
| Indexacao B-tree | Otima | Ruim (aleatorio) | Otima |
| Multi-instancia | Sim (machine ID) | Sim | Nao |
Protecoes implementadas:
- Clock drift: recusa gerar se relogio voltar
- Sequence overflow: aguarda proximo milissegundo se 4096 IDs esgotados
- Machine ID: configuravel via
MACHINE_ID(variavel de ambiente) - Batch:
generateMany(count)para criacao em lote
Schema: 59 Modelos
Diagrama de Relacionamentos
Principais Modelos por Dominio
| Dominio | Modelos | Total |
|---|---|---|
| Usuarios e Auth | User, Session | 2 |
| Financeiro | Transaction, Deposit, Withdrawal | 3 |
| Caixas | Case, CaseCategory, CaseItem, CaseOpening, Item, UserInventory | 6 |
| Batalhas | Battle, BattlePlayer, BattleCase, BattleRound, BattleSettlement | 5 |
| Upgrades | Upgrade, UpgradeItem | 2 |
| Swaps | Swap, SwapItem, SiteSwap, SiteSwapItem | 4 |
| Sorteios | Raffle, RafflePrize, RaffleTicket | 3 |
| Eventos | Event, EventLevel, EventProgress, EventReward, EventShootItem, EventShootOpening | 6 |
| Saques | WaxpeerWithdrawal | 1 |
| Notificacoes | Notification | 1 |
| Auditoria | AuditLog | 1 |
| Subtotal (nucleo) | 34 |
O codigo atual possui 59 modelos (grep '^model ' prisma/schema.prisma). Alem do nucleo acima, os subsistemas mais recentes adicionam modelos proprios:
| Dominio | Modelos | Total |
|---|---|---|
| Crash | CrashRound, CrashBet, CrashBetSkin, CrashCycle, CrashRoundProfitDecision, CrashSeedForge, CrashProfitPoolConfig | 7 |
| Mines | MinesGame, MinesConfig, MinesSeed, MinesBetSkin | 4 |
| Deposito Cripto (CoinPayments) | CryptoDeposit, CryptoDepositConfig | 2 |
| Deposito Skins (SkinsBack) | SkinsBackDeposit, SkinsBackDepositConfig | 2 |
| Saque Automatico (Waxpeer) | WithdrawalAutoApprovalConfig, UserWithdrawalLimits | 2 |
35 Enums Tipados
Enums no PostgreSQL previnem estados invalidos na camada de banco. Qualquer valor fora do enum causa erro no INSERT/UPDATE. A tabela lista os enums principais; o codigo atual define 35 enums (grep '^enum ' prisma/schema.prisma).
| Enum | Valores | Uso |
|---|---|---|
| UserStatus | ACTIVE, SUSPENDED, BANNED | Estado da conta |
| UserRole | USER, ADMIN, SUPER_ADMIN | Nivel de acesso |
| TransactionType | DEBIT, CREDIT | Double-entry |
| TransactionStatus | PENDING, COMPLETED, FAILED, CANCELLED | Estado da transacao |
| PaymentMethod | PIX, CREDIT_CARD, CRYPTO | Metodo de deposito |
| DepositStatus | PENDING, CONFIRMED, FAILED, EXPIRED | Estado do deposito |
| WithdrawalMethod | STEAM_TRADE, PIX | Metodo de saque |
| WithdrawalStatus | PENDING_2FA, PROCESSING, COMPLETED, FAILED, CANCELLED | Estado do saque |
| CaseStatus | ACTIVE, INACTIVE, COMING_SOON | Disponibilidade da caixa |
| RiskLevel | LOW, MEDIUM, HIGH, MAXIMUM | Nivel de risco |
| Rarity | CONSUMER, INDUSTRIAL, MIL_SPEC, RESTRICTED, CLASSIFIED, COVERT, EXTRAORDINARY | Raridade do item |
| WeaponCategory | PISTOL, RIFLE, SMG, SNIPER, SHOTGUN, HEAVY, KNIFE, GLOVE, OTHER | Tipo de arma |
| ItemExterior | FACTORY_NEW, MINIMAL_WEAR, FIELD_TESTED, WELL_WORN, BATTLE_SCARRED, NOT_PAINTED | Condicao |
| InventoryItemStatus | AVAILABLE, LOCKED, SOLD, WITHDRAWN, USED_IN_UPGRADE, USED_IN_BATTLE, IN_SWAP, IN_CRASH, USED_IN_CRASH, IN_MINES, USED_IN_MINES | Estado do item |
| WaxpeerWithdrawalStatus | PENDING_APPROVAL, PENDING, WAITING_SELLER, TRADE_SENT, COMPLETED, CANCELLED, FAILED | Tracking de saque |
| NotificationType | DEPOSIT_CONFIRMED, WITHDRAWAL_COMPLETED, WITHDRAWAL_FAILED, CASE_OPENING, UPGRADE_SUCCESS, UPGRADE_FAIL, SYSTEM, PROMOTION | Tipo de notificacao |
| NotificationStatus | UNREAD, READ, ARCHIVED | Estado da notificacao |
| BattleMode | NORMAL, FLIP, SHARED | Modo da batalha |
| BattleType | ONE_VS_ONE, TWO_VS_TWO, THREE_VS_THREE, FOUR_VS_FOUR, ONE_VS_ONE_VS_ONE, ONE_VS_ONE_VS_ONE_VS_ONE | Formato |
| BattleStatus | WAITING, IN_PROGRESS, FINISHED, CANCELLED | Estado da batalha |
| BattleSettlementType | DROPPED, REMOVED_TO_POOL, ADDED_FROM_POOL | Tipo de distribuicao |
| SwapStatus | PENDING, ACCEPTED, DECLINED, CANCELLED, EXPIRED | Estado da troca |
| RaffleStatus | PENDING, ACTIVE, DRAWING, COMPLETED, CANCELLED | Estado do sorteio |
| EventType | BATTLE_PASS, SEASONAL_EVENT, SPECIAL_EVENT | Tipo de evento |
| EventStatus | UPCOMING, ACTIVE, ENDED | Estado do evento |
| RewardType | ITEM, BALANCE, TICKETS, BADGE | Tipo de recompensa |
| SkinsBackDepositStatus | PENDING, WAITING, IN_HOLD, HOLD_APPROVED, COMPLETED, FAILED, RETURNED | Deposito de skins (SkinsBack) |
| CryptoDepositStatus | PENDING, AWAITING_PAYMENT, PARTIAL, CONFIRMING, PAID, COMPLETED, CANCELLED, EXPIRED, FAILED | Deposito cripto (CoinPayments) |
| CrashCycleStatus | ACTIVE, COMPLETED, ABORTED | Ciclo de profit-control do Crash |
| CrashProfitMode | DISABLED, FREE_BOTS_ONLY, FREE_PLAYERS, FORCE_CRASH_CYCLE, FORCE_CRASH_POOL_DEPLETED, FORCE_CRASH_BUDGET_GUARD | Modo de decisao por rodada do Crash |
17 Unique Constraints
Constraints de unicidade garantem que dados criticos nunca sejam duplicados, independente de logica de aplicacao.
Campo Unico
| Tabela | Campo | Protege contra |
|---|---|---|
| User | steamId | Conta duplicada |
| User | referralCode | Codigo de indicacao duplicado |
| Transaction | relatedTransactionId | Double-entry orfao |
| Deposit | externalId | Deposito processado 2x |
| CaseCategory | name | Categoria duplicada |
| CaseCategory | slug | Slug duplicado |
| Case | slug | URL duplicada |
| Item | marketHashName | Item duplicado no catalogo |
| WaxpeerWithdrawal | waxpeerId | Tracking duplicado |
| WaxpeerWithdrawal | projectId | Projeto duplicado |
| Battle | joinCode | Codigo de entrada duplicado |
Constraint Composta
| Tabela | Campos | Protege contra |
|---|---|---|
| CaseItem | [caseId, itemId] | Mesmo item 2x na caixa |
| BattlePlayer | [battleId, userId] | Jogador entrando 2x |
| RaffleTicket | [raffleId, ticketNumber] | Numero de ticket duplicado |
| EventLevel | [eventId, level] | Nivel duplicado no evento |
| EventProgress | [eventId, userId] | Progresso duplicado |
| EventReward | [eventLevelId, userId] | Recompensa recebida 2x |
17 Cascade Deletes
Quando uma entidade pai e deletada, todas as entidades filhas sao removidas automaticamente pelo PostgreSQL, garantindo integridade referencial sem orfaos.
| Entidade Pai | Entidade Filha | Quando Pai e Deletado |
|---|---|---|
| User | Session | Todas as sessoes removidas |
| User | Notification | Notificacoes removidas |
| User | EventProgress | Progresso removido |
| User | EventReward | Recompensas removidas |
| Case | CaseItem | Items da caixa removidos |
| Upgrade | UpgradeItem | Items do upgrade removidos |
| Battle | BattlePlayer | Jogadores removidos |
| Battle | BattleCase | Cases da batalha removidas |
| Battle | BattleRound | Rounds removidos |
| Battle | BattleSettlement | Settlements removidos |
| Swap | SwapItem | Items da troca removidos |
| SiteSwap | SiteSwapItem | Items da troca removidos |
| Raffle | RafflePrize | Premios removidos |
| Raffle | RaffleTicket | Tickets removidos |
| Event | EventLevel | Niveis removidos |
| Event | EventProgress | Progresso removido |
| EventLevel | EventReward | Recompensas removidas |
Integridade Financeira
Double-Entry Bookkeeping
Toda operacao financeira gera dois registros atomicos na tabela Transaction: um DEBIT e um CREDIT pareados via relatedTransactionId.
Abertura de Caixa (R$ 15,90):
DEBIT → userId: 123, amountCents: 1590, reason: "Case opening #456"
CREDIT → userId: 0 (SYSTEM), amountCents: 1590, relatedTransactionId: <debit_id>
Invariante: SUM(CREDITS) - SUM(DEBITS) = 0 (sistema inteiro)Metodos do TransactionService:
| Metodo | De | Para | Uso |
|---|---|---|---|
debit(userId, amount) | Usuario | Sistema (ID 0) | Abertura, upgrade, batalha |
credit(userId, amount) | Sistema (ID 0) | Usuario | Deposito, venda de item |
transfer(from, to, amount) | Usuario | Usuario | Transferencia P2P |
createDoubleEntry(debit, credit, amount) | Customizavel | Customizavel | Operacoes especiais |
Source of Truth do Saldo
O saldo real e sempre calculado por SUM(CREDITS) - SUM(DEBITS) na tabela Transaction. O campo user.balanceCents e apenas um cache atualizado apos cada transacao.
Source of Truth: TransactionRepository.getUserBalance(userId) → GROUP BY
Cache: User.balanceCents (atualizado apos cada TX, NAO confiavel para operacoes)Distributed Locks (Redlock)
Locks distribuidos via Redis previnem race conditions em operacoes financeiras. Padrao: Lock → Prisma $transaction → Double-Entry → Commit → Release.
| Lock | TTL | Retries | Uso |
|---|---|---|---|
user:balance:{userId} | 10s | 10x (200ms + 200ms jitter) | Toda operacao de saldo |
transaction:create | 5s | 10x | Criacao de double-entry |
withdrawal:{inventoryItemId} | 30s | 10x | Saque de item |
Configuracao Redlock:
- Drift factor: 0.01 (1% tolerancia de clock)
- Auto-extension: threshold de 500ms (estende lock se < 500ms restantes)
Audit Log com Hash Chaining
Cada registro de auditoria inclui o hash SHA-256 do registro anterior, formando uma cadeia imutavel verificavel. Alteracao em qualquer registro intermediario quebra a cadeia.
Log #1: currentHash = SHA256("data1")
Log #2: currentHash = SHA256("hash1:data2")
Log #3: currentHash = SHA256("hash2:data3")
...Verificacao: GET /api/audit/verify-chain percorre a cadeia e valida cada hash. Se qualquer registro foi alterado, retorna o ID do registro corrompido.
Transacoes Prisma
Todas as operacoes financeiras rodam dentro de prisma.$transaction():
| Parametro | Valor |
|---|---|
maxWait | 10 segundos (tempo maximo na fila de conexoes) |
timeout | 30 segundos (tempo maximo de execucao da transacao) |
| Isolation | Read Committed (padrao PostgreSQL) |
Padrao no codigo: Use cases aceitam externalTx?: PrismaTransactionClient para composicao de transacoes, permitindo que multiplas operacoes rodem na mesma transacao atomica.
Performance: 49+ Indexes
A migration 20260202120000_add_performance_indexes cria 49 indexes adicionais aos indexes do Prisma schema, incluindo 1 extensao PostgreSQL.
Extensao PostgreSQL
CREATE EXTENSION IF NOT EXISTS pg_trgm;Habilita busca por similaridade de trigramas, permitindo queries fuzzy com performance de index invertido.
Distribuicao por Tipo
| Tipo | Quantidade | Beneficio |
|---|---|---|
| Composite (multi-coluna) | 28 | Cobrem queries com multiplos filtros |
| Partial (com WHERE) | 16 | Indexam apenas registros relevantes, tamanho reduzido |
| Standard B-tree | 4 | Queries simples de campo unico |
| GIN (trigram) | 1 | Busca fuzzy/parcial por nome |
Indexes Criticos por Sistema
Provably Fair (Abertura de Caixas)
CREATE INDEX idx_case_items_case_hash_range
ON case_items (case_id, hash_range_start, hash_range_end);Lookup O(1) para encontrar item baseado no roll. Sem este index, cada abertura faria scan de todos os items da caixa.
Sistema FLIP
CREATE INDEX idx_case_items_rare
ON case_items (case_id, is_rare) WHERE is_rare = true;Partial index sobre itens do tier raro (is_rare). O FLIP real e threshold por caixa (roll >= case.flipThreshold, com roll unico — sem segundo sorteio independente); o index acelera a montagem da roleta com os itens do tier raro (topo dos hash ranges).
Calculo de Saldo
CREATE INDEX idx_transactions_user_type_status
ON transactions (user_id, type, status);Cobre a query de getUserBalance() que faz GROUP BY type WHERE userId = X AND status = 'COMPLETED'.
Inventario do Usuario
CREATE INDEX idx_inventory_available
ON user_inventory (user_id, status, item_id) WHERE status = 'AVAILABLE';Partial index: indexa apenas items AVAILABLE. Como a maioria dos items eventualmente sai do inventario (vendido, sacado, usado), este index permanece compacto mesmo com milhoes de registros totais.
Lobby de Batalhas
CREATE INDEX idx_battles_public_waiting
ON battles (status, is_private, created_at DESC)
WHERE status = 'WAITING' AND is_private = false;Partial index para o lobby: indexa apenas batalhas publicas esperando jogadores. A maioria das batalhas esta FINISHED, entao este index cobre <1% da tabela.
Live Drops
CREATE INDEX idx_case_openings_recent ON case_openings (created_at DESC);
CREATE INDEX idx_case_openings_item ON case_openings (item_id, created_at DESC);Queries de live drops (aberturas recentes) sao servidas diretamente pelo index sem acessar a tabela (index-only scan).
Busca Fuzzy de Items
CREATE INDEX idx_items_name_trgm ON items USING gin (name gin_trgm_ops);GIN index com trigramas. Permite busca como WHERE name % 'dragon' ou WHERE name ILIKE '%drago%' com performance sub-milissegundo.
Todos os 49 Indexes
| # | Index | Tabela | Tipo |
|---|---|---|---|
| 1 | idx_users_role | users | B-tree |
| 2 | idx_users_referred_by | users | Partial |
| 3 | idx_users_total_wagered | users | B-tree DESC |
| 4 | idx_users_balance | users | B-tree DESC |
| 5 | idx_transactions_reason | transactions | B-tree |
| 6 | idx_transactions_user_type_status | transactions | Composite |
| 7 | idx_transactions_related_user | transactions | Partial |
| 8 | idx_items_name_trgm | items | GIN (pg_trgm) |
| 9 | idx_items_market_hash | items | B-tree |
| 10 | idx_items_rarity_value | items | Composite |
| 11 | idx_items_weapon_rarity | items | Composite |
| 12 | idx_items_collection | items | Partial |
| 13 | idx_items_stattrak | items | Partial |
| 14 | idx_items_value_brl | items | B-tree |
| 15 | idx_case_items_case_hash_range | case_items | Composite |
| 16 | idx_case_items_rare | case_items | Partial |
| 17 | idx_cases_public_list | cases | Composite Partial |
| 18 | idx_cases_slug | cases | B-tree |
| 19 | idx_case_openings_recent | case_openings | B-tree DESC |
| 20 | idx_case_openings_item | case_openings | Composite |
| 21 | idx_case_openings_profit | case_openings | B-tree |
| 22 | idx_inventory_available | user_inventory | Composite Partial |
| 23 | idx_inventory_locked | user_inventory | Partial |
| 24 | idx_inventory_acquired_from | user_inventory | Partial |
| 25 | idx_battles_public_waiting | battles | Composite Partial |
| 26 | idx_battles_mode | battles | Composite |
| 27 | idx_battles_type | battles | Composite |
| 28 | idx_battles_winner | battles | Partial |
| 29 | idx_battle_players_team | battle_players | Composite |
| 30 | idx_battle_rounds_item | battle_rounds | B-tree |
| 31 | idx_battle_rounds_flip | battle_rounds | Composite Partial |
| 32 | idx_battle_settlements_user_type | battle_settlements | Composite |
| 33 | idx_upgrades_recent | upgrades | B-tree DESC |
| 34 | idx_upgrades_success_user | upgrades | Composite |
| 35 | idx_upgrades_target | upgrades | B-tree |
| 36 | idx_swaps_public_active | swaps | Composite Partial |
| 37 | idx_raffles_active | raffles | Composite Partial |
| 38 | idx_raffles_prize | raffles | Partial |
| 39 | idx_raffle_tickets_user_raffle | raffle_tickets | Composite |
| 40 | idx_deposits_pending | deposits | Composite Partial |
| 41 | idx_deposits_method | deposits | Composite |
| 42 | idx_withdrawals_pending | withdrawals | Composite Partial |
| 43 | idx_withdrawals_method | withdrawals | Composite |
| 44 | idx_notifications_unread_type | notifications | Composite Partial |
| 45 | idx_events_active | events | Composite Partial |
| 46 | idx_event_progress_ranking | event_progress | Composite |
| 47 | idx_audit_entity | audit_logs | Composite |
| 48 | idx_audit_action_date | audit_logs | Composite |
| 49 | idx_sessions_device | sessions | Partial |
Redis: Cache, Locks e Pub/Sub
Redis serve como camada complementar ao PostgreSQL para operacoes que exigem latencia sub-milissegundo.
Funcoes do Redis
| Funcao | Uso | TTL |
|---|---|---|
| Sessions | Autenticacao com binding de IP/UA | 7 dias de inatividade |
| Distributed Locks | Redlock para operacoes financeiras | 5-30s por lock |
| Cache Waxpeer | Snapshot de items do marketplace | Configuravel |
| Pub/Sub | Relay de eventos WebSocket do Crash entre containers (canais crash:ws:emit:*) | N/A |
| Exchange Rates | Taxa USD/BRL em cache | Configuravel |
Separacao de Concerns
Redis DB 0: Sessions + Locks + Cache (padrao)
PostgreSQL: Source of Truth (financeiro, items, auditoria)Regra: Redis e efemero. Se Redis reiniciar, o sistema perde sessions (usuarios precisam relogar) e locks (operacoes em andamento falham de forma segura), mas ZERO dados financeiros sao perdidos.
Migrations
O schema evolui via Prisma Migrate. Cada migration e um arquivo SQL versionado e imutavel.
Historico de Migrations (14)
| # | Data | Migration | Descricao |
|---|---|---|---|
| 1 | 2025-11-29 | init | Schema inicial completo |
| 2 | 2025-12-02 | add_bg_color_to_cases | Cor de fundo das caixas |
| 3 | 2025-12-03 | add_flip_provably_fair | Campos provably fair do FLIP |
| 4 | 2025-12-03 | add_is_rare_to_case_item | Flag is_rare para sistema FLIP |
| 5 | 2025-12-04 | add_battle_settlement | Sistema de distribuicao justa |
| 6 | 2025-12-04 | add_battle_round_replay_fields | Dados de replay de batalha |
| 7 | 2025-12-04 | add_balance_used_cents_to_upgrade | Rastreio de saldo em upgrades |
| 8 | 2025-12-05 | remove_autoincrement_use_snowflake | Migracao para Snowflake IDs |
| 9 | 2025-12-05 | add_acquired_from | Rastreio de origem do item |
| 10 | 2025-12-06 | add_site_swaps_table | Sistema de swaps do site |
| 11 | 2026-01-30 | add_value_cents_brl | Precos em BRL (Real) |
| 12 | 2026-02-02 | add_performance_indexes | 49 indexes + pg_trgm |
| 13 | 2026-02-02 | add_original_price_margin | Preco original e margem |
| 14 | 2026-02-11 | add_per_item_raffle_winners | Vencedores por item no sorteio |
Resumo de Robustez
| Mecanismo | Implementacao | Cobertura |
|---|---|---|
| Double-Entry Bookkeeping | TransactionService | 100% operacoes financeiras |
| Distributed Locks | Redlock (10 retries, auto-extend) | Saldo, transacoes, saques |
| BIGINT centavos | Prisma schema | 100% campos monetarios |
| Audit Log imutavel | Hash chaining SHA-256 | Todas as operacoes auditaveis |
| Snowflake IDs | 64-bit (41+10+12 bits) | 100% das tabelas |
| Prisma Transactions | maxWait 10s, timeout 30s | Todas as operacoes criticas |
| pg_trgm + GIN | Extensao PostgreSQL | Busca fuzzy de items |
| 49+ Indexes | 16 Partial, 28 Composite, 1 GIN | Todas as queries criticas |
| 35 Enums tipados | PostgreSQL native enums | Prevencao de estados invalidos |
| 17 Unique Constraints | Campo e composta | Prevencao de duplicatas |
| 17 Cascade Deletes | Foreign keys | Integridade referencial |
| 59 Modelos | Prisma schema | Dominio completo |
| 14 Migrations | Versionamento SQL | Evolucao controlada |
| Sessions Redis + IP/UA | Binding por sessao | Deteccao de hijacking |
| Validacao de entrada | ValidationPipe + SanitizePipe | Todos os endpoints |
| 31 Repositories (infra) + 30 interfaces (domain) | Clean Architecture | Separacao dominio/infra |
