fraudshield/specs/001-fraud-detection-engine/data-model.md
Felipe Domingues 7f9832bf4b plan: add implementation plan for fraud detection engine
Stack: Python 3.12/FastAPI + React 19/TypeScript + PostgreSQL + Redis.
6 artifacts: plan.md, research.md, data-model.md, api.yaml (OpenAPI),
quickstart.md, updated AGENTS.md. Constitution check: 6/6 PASS.
2026-05-12 14:58:21 -03:00

9.2 KiB

Data Model: Motor de Detecção de Fraudes Bancárias

Feature: 001-fraud-detection-engine Date: 2026-05-11

Entity Relationship Diagram

┌─────────────┐       ┌──────────────────┐       ┌─────────────┐
│ Transaction  │──────▶│   FraudAlert     │◀──────│ DetectionRule│
└─────────────┘  1:N  └──────────────────┘  N:M  └─────────────┘
                              │
                              │ 1:1
                              ▼
                     ┌──────────────────┐
                     │InvestigationResult│
                     └──────────────────┘
                              │
                              ▼
                     ┌──────────────────┐
                     │    AuditLog      │ (todos os eventos)
                     └──────────────────┘

Entities

Transaction

Field Type Constraints Description
id UUID PK, NOT NULL Identificador único da transação
external_id VARCHAR(255) NOT NULL, UNIQUE ID original do sistema bancário
amount DECIMAL(15,2) NOT NULL, CHECK > 0 Valor da transação em reais
merchant_name VARCHAR(255) NOT NULL Nome do estabelecimento
merchant_category VARCHAR(100) NOT NULL Categoria (ex: "alimentação", "eletrônicos")
latitude DECIMAL(10,7) NULLABLE Latitude do estabelecimento
longitude DECIMAL(10,7) NULLABLE Longitude do estabelecimento
customer_id VARCHAR(128) NOT NULL, INDEX ID do cliente tokenizado
channel VARCHAR(20) NOT NULL, CHECK (web, mobile, pos) Canal da transação
transaction_at TIMESTAMPTZ NOT NULL, INDEX Timestamp original da transação
ingested_at TIMESTAMPTZ NOT NULL, DEFAULT NOW() Timestamp de ingestão no FraudShield
ruleset_version VARCHAR(20) NOT NULL Versão do conjunto de regras usado
raw_payload JSONB NOT NULL Payload original completo para auditoria

Indexes:

  • idx_transactions_customer_id on customer_id
  • idx_transactions_transaction_at on transaction_at
  • idx_transactions_ingested_at on ingested_at

DetectionRule

Field Type Constraints Description
id UUID PK, NOT NULL Identificador único da regra
name VARCHAR(255) NOT NULL, UNIQUE Nome da regra
description TEXT NOT NULL Descrição do que a regra detecta
condition TEXT NOT NULL Expressão da condição (ex: amount > customer.avg_amount * 3)
weight INTEGER NOT NULL, CHECK 0-100, DEFAULT 0 Peso da regra no score composto
threshold INTEGER NOT NULL, CHECK 0-100, DEFAULT 0 Score mínimo individual para disparar
is_active BOOLEAN NOT NULL, DEFAULT FALSE Se a regra está ativa em produção
version INTEGER NOT NULL, DEFAULT 1 Versão incremental
created_by VARCHAR(255) NOT NULL Usuário que criou
created_at TIMESTAMPTZ NOT NULL, DEFAULT NOW() Data de criação
updated_at TIMESTAMPTZ NOT NULL, DEFAULT NOW() Data da última modificação

FraudAlert

Field Type Constraints Description
id UUID PK, NOT NULL Identificador único do alerta
transaction_id UUID FK → Transaction.id, NOT NULL Transação associada
score INTEGER NOT NULL, CHECK 0-100 Score de risco composto
triggered_rules JSONB NOT NULL Array de objetos: [{rule_id, rule_name, score, explanation}]
explanation TEXT NOT NULL Explicação composta em linguagem natural
status VARCHAR(20) NOT NULL, DEFAULT 'pending', CHECK (pending, confirmed, false_positive, escalated) Status do alerta
assigned_to VARCHAR(255) NULLABLE Analista designado
decision VARCHAR(20) NULLABLE, CHECK (confirmed, false_positive, escalated) Decisão tomada
decided_by VARCHAR(255) NULLABLE Analista que decidiu
decided_at TIMESTAMPTZ NULLABLE Timestamp da decisão
justification TEXT NULLABLE Justificativa (obrigatória para false_positive e escalated)
created_at TIMESTAMPTZ NOT NULL, DEFAULT NOW() Timestamp de criação do alerta
ruleset_version VARCHAR(20) NOT NULL Versão do ruleset que gerou o alerta

Indexes:

  • idx_alerts_status on status
  • idx_alerts_created_at on created_at
  • idx_alerts_transaction_id on transaction_id

AuditLog

Field Type Constraints Description
id UUID PK, NOT NULL Identificador único
trace_id UUID NOT NULL, INDEX Trace ID para correlação
event_type VARCHAR(50) NOT NULL, INDEX Tipo de evento (transaction_received, rule_evaluated, alert_created, decision_made, rule_updated)
event_at TIMESTAMPTZ NOT NULL, INDEX Timestamp do evento
actor VARCHAR(255) NULLABLE Usuário ou sistema que gerou o evento
payload JSONB NOT NULL Dados completos do evento
ruleset_version VARCHAR(20) NULLABLE Versão do ruleset no momento do evento

Partitioning: Por mês via event_at para facilitar retenção e performance.

Imutabilidade: Tabela configurada com triggers que rejeitam UPDATE e DELETE. Apenas INSERT permitido.

RuleSnapshot

Field Type Constraints Description
version VARCHAR(20) PK, NOT NULL Versão do conjunto de regras (ex: "20260511-001")
rules_json JSONB NOT NULL Snapshot completo de todas as regras ativas na versão
created_at TIMESTAMPTZ NOT NULL, DEFAULT NOW() Quando o snapshot foi criado
created_by VARCHAR(255) NOT NULL Quem gerou o snapshot

CustomerProfile (Cache / Materialized View)

Field Type Source Description
customer_id VARCHAR(128) Derived ID do cliente tokenizado
avg_amount_30d DECIMAL(15,2) AVG(amount) 30d Média de gasto 30 dias
avg_amount_90d DECIMAL(15,2) AVG(amount) 90d Média de gasto 90 dias
common_categories JSONB Top 5 categorias 90d Categorias mais frequentes
common_locations JSONB Cluster de coordenadas Regiões habituais de transação
night_transactions_pct DECIMAL(5,2) % transações 22h-06h Percentual noturno
last_updated_at TIMESTAMPTZ NOW() Última atualização

Implementation: Materialized view no PostgreSQL, refresh a cada 5 minutos. Cache em Redis com TTL de 5 minutos para acesso rápido pelo motor de regras.

State Transitions

Alert Lifecycle

                    ┌──────────────┐
                    │   pending    │ ◀── Alerta criado (score > threshold)
                    └──────┬───────┘
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
       ┌──────────┐ ┌──────────┐ ┌───────────┐
       │confirmed │ │  false_  │ │ escalated │
       │          │ │ positive │ │           │
       └──────────┘ └──────────┘ └─────┬─────┘
                                       │
                                 Revisão sênior
                                       │
                              ┌────────┼────────┐
                              ▼                 ▼
                       ┌──────────┐      ┌──────────┐
                       │confirmed │      │  false_  │
                       │          │      │ positive │
                       └──────────┘      └──────────┘

Estados finais: confirmed, false_positive (não transicionam mais)

Rule Lifecycle

  ┌────────┐    activate    ┌────────┐
  │inactive│───────────────▶│ active │
  └────────┘                └────────┘
       ▲                          │
       │      deactivate          │
       └──────────────────────────┘

Validation Rules

  • Transaction.amount > 0
  • Transaction.channel ∈ {web, mobile, pos}
  • DetectionRule.weight ∈ [0, 100]
  • DetectionRule.threshold ∈ [0, 100]
  • FraudAlert.score ∈ [0, 100]
  • FraudAlert.justification required when decision ∈ {false_positive, escalated}
  • FraudAlert.status transitions only: pending → {confirmed, false_positive, escalated}; escalated → {confirmed, false_positive}