Fase 03 — Product Delivery
Arquitetura DDD, API REST, frontend React SPA e qualidade — do design de domínio ao deploy.
5
Bounded Contexts
7
ADRs
27
Endpoints
9
Tabelas
10
Páginas
29
Test Cases
10/10
Quality Gates
Arquitetura — Domain-Driven Design
5 Bounded Contexts em monólito modular Node.js (Fastify). Separação lógica em módulos distintos em /server, com path de extração para microsserviços no futuro.
classDiagram
class IdentityAccess {
+User (aggregate root)
+Session (stateless JWT)
+Email, CPF, HashedPassword
POST /auth/register
POST /auth/login
GET /users/me
}
class BankingCore {
+Account (aggregate root)
+Transaction
+Money, TransactionCategory
GET /accounts/balance
GET /transactions
GET /transactions/summary
}
class Pix {
+PixKey (aggregate root)
+PixTransfer
+PixKeyType, PixLimit
POST /pix/transfer
GET /pix/keys
POST /pix/keys
}
class CardsPayments {
+Card (aggregate root)
+Invoice, CardPurchase
+CardNumber, CardLimit
GET /cards
POST /payments/boleto
}
class Intelligence {
+Notification (aggregate root)
+SalaryCycle, CategoryRule
GET /transactions/summary
}
IdentityAccess --> BankingCore : UserRegistered
IdentityAccess --> Pix : User exists
IdentityAccess --> CardsPayments : User exists
Pix --> BankingCore : debit/credit
CardsPayments --> BankingCore : debit
BankingCore --> Intelligence : events
Pix --> Intelligence : events
CardsPayments --> Intelligence : events
Bounded Contexts
| BC | Nome | Tipo | Módulos | Responsabilidade |
|---|---|---|---|---|
| BC-01 | Identity & Access | Supporting | auth, users | Identidade, autenticação JWT, autorização, ciclo de vida de sessões |
| BC-02 | Banking Core | Core | accounts, transactions | Custódia do saldo, débito/crédito atômicos, ledger imutável, categorização |
| BC-03 | Pix | Core | pix | Chaves Pix, transferências, limites por horário, QR Code |
| BC-04 | Cards & Payments | Supporting | cards, payments | Cartão virtual, faturas, compras, pagamento de boletos |
| BC-05 | Intelligence | Supporting | transactions (summary), notifications | Categorização automática, insights, notificações, ciclo salarial |
Architecture Decision Records (ADRs)
ADR-001
SQLite3 como banco de dados do MVP em vez de PostgreSQL
Status: Accepted
Zero configuração — arquivo único database.sqlite. Sem servidor separado. Transações ACID completas via better-sqlite3 (síncrono). Performance excelente para cargas <100 writes/segundo. Migração para PostgreSQL planejada quando escala horizontal for requisito.
ADR-002
SQL direto via better-sqlite3 em vez de ORM (Prisma, TypeORM, Drizzle)
Status: Accepted
Queries de domínio financeiro (débito + registro de transação) são explícitas e auditáveis. Zero overhead de ORM. Prepared statements automáticos via better-sqlite3 — SQL injection impossível com uso correto.
ADR-003
Autenticação JWT local com bcryptjs em vez de provedor externo
Status: Accepted
Zero dependência externa. CPF e e-mail nunca saem do servidor. Autenticação reforçada (RN-03) implementada como middleware nativo Fastify. Token JWT HS256 com expiry de 24h. bcryptjs (pure JS, saltRounds=10).
ADR-004
SPA com React+Vite em vez de SSR com Next.js
Status: Accepted
Produto web-first autenticado — nenhuma tela precisa de SEO. SPA oferece experiência mais próxima de desktop app. Vite HMR <50ms. Bundle servido pelo próprio Fastify como arquivos estáticos.
ADR-005
Valores monetários como INTEGER em centavos em vez de DECIMAL/FLOAT
Status: Accepted
Padrão Stripe/Adyen/PagSeguro. Precisão absoluta — sem ponto flutuante. R$1.234,56 = 123456 centavos. Limites de Pix verificados com comparação inteira. SQLite INTEGER 64-bit suporta até ~R$92 quadrilhões.
ADR-006
Soft delete com coluna deleted_at em vez de deleção física
Status: Accepted
RN-07 define soft delete como requisito. Histórico de auditoria 100% preservado. Compliance bancário: registros nunca destruídos. Reversibilidade de operações equivocadas.
ADR-007
Convenção de 3 arquivos por módulo: schema.ts + routes.ts + service.ts
Status: Accepted
schema.ts: Zod schemas para validação + tipos TypeScript inferidos. routes.ts: Fastify plugin com rotas (zero lógica de negócio). service.ts: lógica de domínio pura com acesso direto ao banco. Service testável sem servidor HTTP.
Stack Tecnológica
Backend
- Runtime: Node.js 18+
- Framework: Fastify 5.0
- Database: SQLite3 (better-sqlite3) com WAL mode
- Auth: JWT (jsonwebtoken) + bcryptjs
- Validation: Zod 3.23
- Language: TypeScript 5.6 (strict)
- Test: Vitest
Frontend
- Framework: React 18.3
- Build: Vite 5.4
- Routing: React Router 6.26
- Styling: Tailwind CSS 3.4
- Icons: Lucide React
- Language: TypeScript 5.5 (strict)
- State: React hooks + Context
Diagramas de Sequência
Autenticação — Registro e Login
sequenceDiagram
participant U as Usuário (Browser)
participant F as Frontend (React)
participant A as API (Fastify)
participant DB as SQLite
Note over U,DB: Registro de nova conta
U->>F: Preenche nome, email, CPF, senha
F->>A: POST /api/auth/register
A->>DB: SELECT user WHERE email = ?
DB-->>A: null (não existe)
A->>A: bcrypt.hash(senha, 12)
A->>DB: INSERT users + INSERT accounts
DB-->>A: userId, accountId
A->>A: jwt.sign({ id, email })
A-->>F: { token, user }
F->>F: localStorage.set(ecp_token)
F-->>U: Redirect /dashboard
Note over U,DB: Login
U->>F: Preenche email, senha
F->>A: POST /api/auth/login
A->>DB: SELECT user WHERE email = ?
DB-->>A: user (com password_hash)
A->>A: bcrypt.compare(senha, hash)
A->>A: jwt.sign({ id, email }, 7d)
A-->>F: { token, user }
F->>F: localStorage.set(ecp_token)
F-->>U: Redirect /dashboard
Envio de Pix — Fluxo Completo com Validações
sequenceDiagram
participant U as Usuário (Browser)
participant F as Frontend (React)
participant A as API (Fastify)
participant DB as SQLite
U->>F: Informa chave Pix destino
F->>A: GET /api/pix/lookup?key=...
A->>DB: SELECT pix_key WHERE key_value = ?
DB-->>A: { userId, name, keyType }
A-->>F: { name: "João Silva", keyType: "cpf" }
F-->>U: Exibe destinatário
U->>F: Informa valor (ex: R$ 500)
F->>A: POST /api/pix/transfer
Note over A: Middleware: JWT auth
A->>A: Verificar rate limit (RN-10: max 10/hora)
A->>DB: SELECT COUNT pix_rate_limit (ultima hora)
DB-->>A: count = 3 (OK)
A->>A: Verificar limite horário
alt 22h-6h (noturno)
A->>A: RN-02: valor <= R$ 1.000?
else 6h-22h (diurno)
A->>A: RN-01: acumulado dia <= R$ 5.000?
end
alt Valor > R$ 2.000
A-->>F: Requer autenticação reforçada (RN-03)
F-->>U: Modal de confirmação + senha
U->>F: Confirma com senha
F->>A: POST /api/pix/transfer (com confirmação)
end
A->>A: Verificar saldo (RN-04)
A->>DB: SELECT balance_cents FROM accounts
DB-->>A: balance = 150000 (R$ 1.500)
Note over A,DB: Transação atômica (SQLite)
A->>DB: BEGIN TRANSACTION
A->>DB: UPDATE accounts SET balance -= 50000 (remetente)
A->>DB: UPDATE accounts SET balance += 50000 (destinatário)
A->>DB: INSERT transactions (débito remetente)
A->>DB: INSERT transactions (crédito destinatário)
A->>DB: INSERT pix_rate_limit
A->>DB: INSERT notifications (destinatário)
A->>DB: COMMIT
A-->>F: { transactionId, status: "completed", balanceAfter }
F-->>U: Tela de sucesso com comprovante
Compra com Cartão Virtual
sequenceDiagram
participant M as Merchant / FoodFlow
participant A as API (Fastify)
participant DB as SQLite
M->>A: POST /api/cards/:id/purchase
Note over A: { amountCents, description, merchantName }
Note over A: Middleware: JWT auth
A->>DB: SELECT card WHERE id = ? AND user_id = ?
DB-->>A: card { limitCents: 300000, usedCents: 124750, isBlocked: false }
A->>A: Verificar cartão ativo e não bloqueado
A->>A: Verificar limite disponível
Note over A: disponivel = 300000 - 124750 = 175250
alt amountCents > disponível
A-->>M: 400 CARD_LIMIT_EXCEEDED
else amountCents <= disponível
A->>DB: SELECT/CREATE invoice (mês atual, status open)
DB-->>A: invoice { id, totalCents }
Note over A,DB: Transação atômica
A->>DB: BEGIN TRANSACTION
A->>DB: INSERT card_purchases
A->>DB: UPDATE cards SET used_cents += amountCents
A->>DB: UPDATE invoices SET total_cents += amountCents
A->>DB: COMMIT
A-->>M: 201 { purchaseId, status: "completed", availableAfterCents }
end
Pagamento de Boleto
sequenceDiagram
participant U as Usuário (Browser)
participant F as Frontend (React)
participant A as API (Fastify)
participant DB as SQLite
U->>F: Cola código de barras do boleto
F->>F: Decodifica linha digitável (valor, vencimento, beneficiário)
F-->>U: Exibe breakdown de transparência
U->>F: Confirma pagamento
F->>A: POST /api/payments/boleto
Note over A: { barcode, amount, scheduledFor? }
Note over A: Middleware: JWT auth
A->>A: Verificar saldo (RN-04)
A->>DB: SELECT balance_cents FROM accounts
DB-->>A: balance = 423578
alt scheduledFor (agendado)
A->>DB: INSERT transactions (status: pending, scheduled_for)
A-->>F: { transactionId, status: "pending", scheduledFor }
F-->>U: "Boleto agendado para DD/MM"
else Pagamento imediato
Note over A,DB: Transação atômica
A->>DB: BEGIN TRANSACTION
A->>DB: UPDATE accounts SET balance -= amountCents
A->>DB: INSERT transactions (status: completed, category: boleto)
A->>DB: INSERT notifications
A->>DB: COMMIT
A-->>F: { transactionId, status: "completed", balanceAfter }
F-->>U: Tela de sucesso com comprovante
end
Dashboard — Carregamento Inicial
sequenceDiagram
participant U as Usuário (Browser)
participant F as Frontend (React)
participant A as API (Fastify)
participant DB as SQLite
U->>F: Acessa /dashboard
F->>F: Verifica JWT no localStorage
par Chamadas paralelas
F->>A: GET /api/accounts/me/balance
A->>DB: SELECT balance_cents FROM accounts
A-->>F: { balanceCents: 423578 }
and
F->>A: GET /api/transactions?limit=5
A->>DB: SELECT * FROM transactions ORDER BY created_at DESC LIMIT 5
A-->>F: { transactions: [...] }
and
F->>A: GET /api/transactions/summary
A->>DB: SELECT category, SUM(amount) GROUP BY category
A-->>F: { categories: [...], totalIncome, totalExpense }
and
F->>A: GET /api/cards
A->>DB: SELECT * FROM cards WHERE user_id = ?
A-->>F: { cards: [...] }
and
F->>A: GET /api/notifications/unread-count
A->>DB: SELECT COUNT(*) WHERE is_read = 0
A-->>F: { count: 3 }
end
F->>F: Renderiza dashboard completo
F-->>U: Saldo + Donut chart + Transações + Cartão + Notificações
API — 9 Módulos, 27 Endpoints
Auth (3 endpoints)
POST/api/auth/register
POST/api/auth/login
GET/api/auth/me
Accounts (3 endpoints)
GET/api/accounts/me
GET/api/accounts/me/balance
PATCH/api/accounts/me/limit
Pix (5 endpoints)
GET/api/pix/keys
POST/api/pix/keys
DELETE/api/pix/keys/:keyId
POST/api/pix/transfer
GET/api/pix/lookup
Transactions (2 endpoints)
GET/api/transactions
GET/api/transactions/:id
Cards (5 endpoints)
GET/api/cards
GET/api/cards/:id
PATCH/api/cards/:id/limit
PATCH/api/cards/:id/block
GET/api/cards/:id/invoice
Payments (3 endpoints)
POST/api/payments/boleto
GET/api/payments/scheduled
DELETE/api/payments/scheduled/:id
Users (3 endpoints)
GET/api/users/me
PATCH/api/users/me
POST/api/users/me/change-password
Notifications (4 endpoints)
GET/api/notifications
GET/api/notifications/unread-count
PATCH/api/notifications/:id/read
POST/api/notifications/read-all
Dashboard (1 endpoint)
GET/api/dashboard
Modelo Entidade-Relacionamento (MER)
9 tabelas, 12 índices. SQLite3 com WAL mode e foreign keys habilitadas. Valores monetários em centavos (INTEGER).
erDiagram
users {
TEXT id PK
TEXT name
TEXT email UK
TEXT cpf UK
TEXT password_hash
TEXT phone
TEXT avatar_url
INTEGER is_active
TEXT created_at
TEXT updated_at
}
accounts {
TEXT id PK
TEXT user_id FK "UK"
TEXT agency
TEXT number UK
INTEGER balance_cents
INTEGER daily_transfer_limit_cents
INTEGER daily_transferred_cents
TEXT last_transfer_date
INTEGER is_active
TEXT created_at
TEXT updated_at
}
pix_keys {
TEXT id PK
TEXT user_id FK
TEXT account_id FK
TEXT key_type "cpf email phone random"
TEXT key_value UK
INTEGER is_active
TEXT created_at
TEXT deleted_at "soft delete"
}
transactions {
TEXT id PK
TEXT account_id FK
TEXT type "credit debit"
TEXT category "pix boleto card_purchase transfer deposit withdrawal refund fee"
INTEGER amount_cents
INTEGER balance_after_cents
TEXT description
TEXT counterpart_name
TEXT counterpart_document
TEXT counterpart_institution
TEXT pix_key
TEXT pix_key_type
TEXT boleto_code
TEXT status "pending completed failed cancelled"
TEXT scheduled_for
TEXT created_at
}
cards {
TEXT id PK
TEXT user_id FK
TEXT account_id FK
TEXT type "physical virtual"
TEXT card_number
TEXT last4
TEXT card_holder
TEXT card_expiry
INTEGER limit_cents
INTEGER used_cents
INTEGER due_day
INTEGER is_active
INTEGER is_blocked
TEXT created_at
TEXT updated_at
}
invoices {
TEXT id PK
TEXT card_id FK
TEXT reference_month
INTEGER total_cents
TEXT due_date
TEXT status "open closed paid overdue"
TEXT paid_at
TEXT created_at
TEXT updated_at
}
card_purchases {
TEXT id PK
TEXT card_id FK
TEXT invoice_id FK
TEXT description
TEXT merchant_name
TEXT merchant_category
INTEGER amount_cents
INTEGER installments
INTEGER current_installment
TEXT status "pending completed cancelled refunded"
TEXT purchased_at
}
notifications {
TEXT id PK
TEXT user_id FK
TEXT title
TEXT body
TEXT type "transaction security marketing system"
INTEGER is_read
TEXT created_at
}
pix_rate_limit {
TEXT id PK
TEXT account_id FK
TEXT window_start
INTEGER transfer_count
}
users ||--|| accounts : "1:1 possui"
users ||--o{ pix_keys : "1:N registra"
users ||--o{ cards : "1:N possui"
users ||--o{ notifications : "1:N recebe"
accounts ||--o{ pix_keys : "1:N vincula"
accounts ||--o{ transactions : "1:N movimenta"
accounts ||--o{ cards : "1:N associa"
accounts ||--o{ pix_rate_limit : "1:N controla"
cards ||--o{ invoices : "1:N gera"
cards ||--o{ card_purchases : "1:N registra"
invoices ||--o{ card_purchases : "1:N agrupa"
Cardinalidades
| Relacionamento | Tipo | Descrição |
|---|---|---|
| users ↔ accounts | 1:1 | Cada usuário possui exatamente uma conta bancária |
| users ↔ pix_keys | 1:N | Até 5 chaves Pix por usuário (RN-05) |
| users ↔ cards | 1:N | Múltiplos cartões virtuais por usuário |
| users ↔ notifications | 1:N | Notificações de eventos financeiros e segurança |
| accounts ↔ transactions | 1:N | Ledger imutável de movimentações (crédito/débito) |
| accounts ↔ pix_rate_limit | 1:N | Controle de 10 Pix/hora por conta (RN-10) |
| cards ↔ invoices | 1:N | Uma fatura por mês de referência por cartão |
| cards ↔ card_purchases | 1:N | Compras registradas no cartão (merchant + categoria) |
| invoices ↔ card_purchases | 1:N | Compras agrupadas na fatura do mês |
Banco de Dados
SQLite3 com WAL mode e foreign keys. 9 tabelas, 12 índices.
| Tabela | Descrição | Aggregate Root |
|---|---|---|
| users | Dados de identidade (nome, email, CPF, password hash, salary_day) | User (BC-01) |
| accounts | Conta bancária com saldo em centavos, número da conta, limites | Account (BC-02) |
| transactions | Ledger imutável de movimentações (crédito/débito) com categoria | — |
| pix_keys | Chaves Pix (CPF, email, telefone, EVP) com soft delete | PixKey (BC-03) |
| cards | Cartão virtual com limite, status (active/blocked/cancelled) | Card (BC-04) |
| invoices | Faturas mensais do cartão (open/closed/paid) | — |
| card_purchases | Compras registradas no cartão com merchant e categoria | — |
| notifications | Notificações de eventos financeiros com status read/unread | Notification (BC-05) |
| pix_rate_limit | Controle de rate limit de Pix por hora por usuário (RN-10) | — |
Frontend — React SPA
10 Páginas
| Página | Arquivo | Features Principais |
|---|---|---|
| Login | routes/login.tsx | Email + senha, show/hide password, error handling |
| Registro | routes/register.tsx | Nome, email, CPF mask, validação de senha (regex) |
| Dashboard | routes/dashboard.tsx | Saldo show/hide, quick actions, SVG donut chart, card summary, últimas 5 transações |
| Extrato | routes/extrato.tsx | Lista paginada com filtros, category badges, paginação com contador |
| Pix Enviar | routes/pix/enviar.tsx | Fluxo multi-step (chave → valor → confirmar → sucesso), lookup de chave |
| Pix Receber | routes/pix/receber.tsx | Lista de chaves, copy-to-clipboard, dados da conta |
| Pix Chaves | routes/pix/chaves.tsx | CRUD completo, modal, contador 5/5, confirm dialog |
| Cartões | routes/cartoes.tsx | Card visual com gradiente, barra de limite, block/unblock, fatura |
| Pagamentos | routes/pagamentos.tsx | Formulário de boleto, agendamento, lista de agendados |
| Perfil | routes/perfil.tsx | Editar nome/telefone, alterar senha, logout |
9 Componentes
UI Components
- Button: 4 variants (primary, secondary, ghost, danger), 3 sizes, loading spinner
- Card: 2 variants (default, highlighted), 4 paddings, sub-components
- Input: Label, error, hint, left/right icons, forwardRef
- Modal: Escape key, backdrop click, scroll lock, 3 sizes
- Badge: 6 variants (success, warning, danger, info, lime, default)
- Table: Full table primitives
Layout Components
- Sidebar: Desktop nav com 6 itens, user info, logout
- Header: Greeting dinâmico, notificações com dropdown (unread count, polling 30s)
- MobileNav: Bottom nav com 5 itens (lg:hidden)
QA — Qualidade e Testes
Quality Gates — 10/10 Passando
| Gate | Status | Detalhe |
|---|---|---|
| Type Safety | Pass | TypeScript strict em server (39 files) e web (28 files). Zod enforces runtime validation. |
| Input Validation | Pass | Todos 27 endpoints validam inputs via Zod + Fastify validation hooks. |
| Business Rules | Pass | 10/10 RNs implementadas (RN-01 a RN-10). |
| Auth Security | Pass | JWT + bcrypt (cost 10). Auth middleware em todas rotas protegidas. CORS configurado. |
| Error Handling | Pass | AppError com ErrorCode enum (15 categorias). ZodError transformado. Safe 500 responses. |
| Design System | Pass | 100% compliance com design_spec.md. Dark theme, lime accent, Inter font. |
| API Integration | Pass | Web integra com todos 27 endpoints via fetch wrapper centralizado (api.ts). |
| Responsive | Pass | Sidebar desktop (≥1024px) + bottom nav mobile. Testado: 375px, 768px, 1024px, 1440px. |
| Test Coverage | Pass | 29 test cases em 4 files. Módulos de maior risco: auth (10), pix (11), accounts (4), payments (4). |
| Accessibility | Pass* | Semantic HTML, form labels, color contrast AA. *Notas: skip-to-content e aria-labels em icon buttons pendentes. |
Findings (5)
| Severidade | Categoria | Descrição |
|---|---|---|
| low | Test Coverage | 5 módulos (transactions, cards, users, notifications, dashboard) sem test files dedicados. Os 4 módulos testados cobrem funcionalidade de maior risco. |
| info | Accessibility | Falta skip-to-content link, focus management em route changes, e aria-label em icon buttons. |
| info | Performance | Fetch wrapper sem caching/deduplication. Considerar React Query ou SWR em iteração futura. |
| low | Security | JWT em localStorage (vs. httpOnly cookies). Aceitável para MVP sem scripts terceiros. |
| info | Observability | Logging via console sem formato estruturado. Integrar pino (nativo Fastify) na Fase 04. |
HITLs da Fase 03
| HITL | Escopo | Agente | Decisão |
|---|---|---|---|
| #7 | Arquitetura DDD (5 BCs, 7 ADRs, linguagem ubíqua, agregados) | Software Architect | Aprovado |
| #8 | Backend (9 módulos, 27 endpoints, 10/10 RNs, 9 tabelas, seed) | Backend Developer | Aprovado |
| #9 | Frontend (10 páginas, 9 componentes, design system compliance) | Frontend Developer | Aprovado |
| #10 | QA (29 test cases, 10/10 quality gates, 5 findings info/low) | QA | Aprovado |