Phase 03 — Product Delivery
HITLs #7–#10 Approved Architect + Backend + Frontend + QA 🇧🇷 PT

Phase 03 — Product Delivery

DDD architecture, REST API, React SPA frontend and quality — from domain design to deploy.

5
Bounded Contexts
7
ADRs
27
Endpoints
9
Tables
10
Pages
29
Test Cases
10/10
Quality Gates

Architecture — Domain-Driven Design

5 Bounded Contexts in a modular Node.js monolith (Fastify). Logical separation in distinct modules under /server, with extraction path to microservices in the future.

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

BCNameTypeModulesResponsibility
BC-01Identity & AccessSupportingauth, usersIdentity, JWT authentication, authorization, session lifecycle
BC-02Banking CoreCoreaccounts, transactionsBalance custody, atomic debit/credit, immutable ledger, categorization
BC-03PixCorepixPix keys, transfers, time-based limits, QR Code
BC-04Cards & PaymentsSupportingcards, paymentsVirtual card, invoices, purchases, bill payment
BC-05IntelligenceSupportingtransactions (summary), notificationsAutomatic categorization, insights, notifications, payroll cycle

Architecture Decision Records (ADRs)

ADR-001
SQLite3 as the MVP database instead of PostgreSQL
Status: Accepted
Zero configuration — single file database.sqlite. No separate server. Full ACID transactions via better-sqlite3 (synchronous). Excellent performance for loads <100 writes/second. Migration to PostgreSQL planned when horizontal scale is a requirement.
ADR-002
Direct SQL via better-sqlite3 instead of ORM (Prisma, TypeORM, Drizzle)
Status: Accepted
Financial domain queries (debit + transaction record) are explicit and auditable. Zero ORM overhead. Automatic prepared statements via better-sqlite3 — SQL injection impossible with correct usage.
ADR-003
Local JWT authentication with bcryptjs instead of external provider
Status: Accepted
Zero external dependency. CPF and email never leave the server. Enhanced authentication (BR-03) implemented as native Fastify middleware. JWT HS256 token with 24h expiry. bcryptjs (pure JS, saltRounds=10).
ADR-004
SPA with React+Vite instead of SSR with Next.js
Status: Accepted
Authenticated web-first product — no screen needs SEO. SPA delivers an experience closer to a desktop app. Vite HMR <50ms. Bundle served by Fastify itself as static files.
ADR-005
Monetary values as INTEGER in cents instead of DECIMAL/FLOAT
Status: Accepted
Stripe/Adyen/PagSeguro standard. Absolute precision — no floating point. R$1,234.56 = 123456 cents. Pix limits checked with integer comparison. SQLite INTEGER 64-bit supports up to ~R$92 quadrillion.
ADR-006
Soft delete with deleted_at column instead of physical deletion
Status: Accepted
BR-07 defines soft delete as a requirement. 100% audit history preserved. Banking compliance: records are never destroyed. Reversibility of mistaken operations.
ADR-007
3-file convention per module: schema.ts + routes.ts + service.ts
Status: Accepted
schema.ts: Zod schemas for validation + inferred TypeScript types. routes.ts: Fastify plugin with routes (zero business logic). service.ts: pure domain logic with direct database access. Service testable without HTTP server.

Technology Stack

Backend
  • Runtime: Node.js 18+
  • Framework: Fastify 5.0
  • Database: SQLite3 (better-sqlite3) with 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

Sequence Diagrams

Authentication — Registration and Login

sequenceDiagram participant U as User (Browser) participant F as Frontend (React) participant A as API (Fastify) participant DB as SQLite Note over U,DB: New account registration U->>F: Fills name, email, CPF, password F->>A: POST /api/auth/register A->>DB: SELECT user WHERE email = ? DB-->>A: null (does not exist) A->>A: bcrypt.hash(password, 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: Fills email, password F->>A: POST /api/auth/login A->>DB: SELECT user WHERE email = ? DB-->>A: user (with password_hash) A->>A: bcrypt.compare(password, hash) A->>A: jwt.sign({ id, email }, 7d) A-->>F: { token, user } F->>F: localStorage.set(ecp_token) F-->>U: Redirect /dashboard

Send Pix — Full Flow with Validations

sequenceDiagram participant U as User (Browser) participant F as Frontend (React) participant A as API (Fastify) participant DB as SQLite U->>F: Enters destination Pix key F->>A: GET /api/pix/lookup?key=... A->>DB: SELECT pix_key WHERE key_value = ? DB-->>A: { userId, name, keyType } A-->>F: { name: "John Smith", keyType: "cpf" } F-->>U: Shows recipient U->>F: Enters amount (e.g. R$500) F->>A: POST /api/pix/transfer Note over A: Middleware: JWT auth A->>A: Check rate limit (BR-10: max 10/hour) A->>DB: SELECT COUNT pix_rate_limit (last hour) DB-->>A: count = 3 (OK) A->>A: Check time-based limit alt 10 PM to 6 AM (nighttime) A->>A: BR-02: amount <= R$1,000? else 6 AM to 10 PM (daytime) A->>A: BR-01: daily total <= R$5,000? end alt Amount > R$2,000 A-->>F: Requires enhanced authentication (BR-03) F-->>U: Confirmation modal + password U->>F: Confirms with password F->>A: POST /api/pix/transfer (with confirmation) end A->>A: Check balance (BR-04) A->>DB: SELECT balance_cents FROM accounts DB-->>A: balance = 150000 (R$1,500) Note over A,DB: Atomic transaction (SQLite) A->>DB: BEGIN TRANSACTION A->>DB: UPDATE accounts SET balance -= 50000 (sender) A->>DB: UPDATE accounts SET balance += 50000 (recipient) A->>DB: INSERT transactions (sender debit) A->>DB: INSERT transactions (recipient credit) A->>DB: INSERT pix_rate_limit A->>DB: INSERT notifications (recipient) A->>DB: COMMIT A-->>F: { transactionId, status: "completed", balanceAfter } F-->>U: Success screen with receipt

Virtual Card Purchase

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: Check card active and not blocked A->>A: Check available limit Note over A: available = 300000 - 124750 = 175250 alt amountCents > available A-->>M: 400 CARD_LIMIT_EXCEEDED else amountCents <= available A->>DB: SELECT/CREATE invoice (current month, status open) DB-->>A: invoice { id, totalCents } Note over A,DB: Atomic transaction 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

Bill Payment

sequenceDiagram participant U as User (Browser) participant F as Frontend (React) participant A as API (Fastify) participant DB as SQLite U->>F: Pastes bill barcode F->>F: Decodes barcode (amount, due date, beneficiary) F-->>U: Shows transparency breakdown U->>F: Confirms payment F->>A: POST /api/payments/boleto Note over A: { barcode, amount, scheduledFor? } Note over A: Middleware: JWT auth A->>A: Check balance (BR-04) A->>DB: SELECT balance_cents FROM accounts DB-->>A: balance = 423578 alt scheduledFor (scheduled) A->>DB: INSERT transactions (status: pending, scheduled_for) A-->>F: { transactionId, status: "pending", scheduledFor } F-->>U: "Bill scheduled for MM/DD" else Immediate payment Note over A,DB: Atomic transaction 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: Success screen with receipt end

Dashboard — Initial Load

sequenceDiagram participant U as User (Browser) participant F as Frontend (React) participant A as API (Fastify) participant DB as SQLite U->>F: Navigates to /dashboard F->>F: Checks JWT in localStorage par Parallel calls 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: Renders full dashboard F-->>U: Balance + Donut chart + Transactions + Card + Notifications

API — 9 Modules, 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

Entity-Relationship Model (ERM)

9 tables, 12 indexes. SQLite3 with WAL mode and foreign keys enabled. Monetary values in cents (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 owns" users ||--o{ pix_keys : "1:N registers" users ||--o{ cards : "1:N owns" users ||--o{ notifications : "1:N receives" accounts ||--o{ pix_keys : "1:N links" accounts ||--o{ transactions : "1:N moves" accounts ||--o{ cards : "1:N associates" accounts ||--o{ pix_rate_limit : "1:N controls" cards ||--o{ invoices : "1:N generates" cards ||--o{ card_purchases : "1:N records" invoices ||--o{ card_purchases : "1:N groups"

Cardinalities

RelationshipTypeDescription
users ↔ accounts1:1Each user owns exactly one bank account
users ↔ pix_keys1:NUp to 5 Pix keys per user (BR-05)
users ↔ cards1:NMultiple virtual cards per user
users ↔ notifications1:NNotifications of financial and security events
accounts ↔ transactions1:NImmutable movement ledger (credit/debit)
accounts ↔ pix_rate_limit1:NControl of 10 Pix/hour per account (BR-10)
cards ↔ invoices1:NOne invoice per reference month per card
cards ↔ card_purchases1:NPurchases recorded on the card (merchant + category)
invoices ↔ card_purchases1:NPurchases grouped in the month's invoice

Database

SQLite3 with WAL mode and foreign keys. 9 tables, 12 indexes.

TableDescriptionAggregate Root
usersIdentity data (name, email, CPF, password hash, salary_day)User (BC-01)
accountsBank account with balance in cents, account number, limitsAccount (BC-02)
transactionsImmutable movement ledger (credit/debit) with category
pix_keysPix keys (CPF, email, phone, EVP) with soft deletePixKey (BC-03)
cardsVirtual card with limit, status (active/blocked/cancelled)Card (BC-04)
invoicesMonthly card invoices (open/closed/paid)
card_purchasesPurchases recorded on the card with merchant and category
notificationsFinancial event notifications with read/unread statusNotification (BC-05)
pix_rate_limitPix rate limit control per hour per user (BR-10)

Frontend — React SPA

10 Pages

PageFileMain Features
Loginroutes/login.tsxEmail + password, show/hide password, error handling
Registerroutes/register.tsxName, email, CPF mask, password validation (regex)
Dashboardroutes/dashboard.tsxBalance show/hide, quick actions, SVG donut chart, card summary, last 5 transactions
Statementroutes/extrato.tsxPaginated list with filters, category badges, pagination with counter
Pix Sendroutes/pix/enviar.tsxMulti-step flow (key → amount → confirm → success), key lookup
Pix Receiveroutes/pix/receber.tsxKey list, copy-to-clipboard, account data
Pix Keysroutes/pix/chaves.tsxFull CRUD, modal, counter 5/5, confirm dialog
Cardsroutes/cartoes.tsxCard visual with gradient, limit bar, block/unblock, invoice
Paymentsroutes/pagamentos.tsxBill form, scheduling, scheduled payments list
Profileroutes/perfil.tsxEdit name/phone, change password, logout

9 Components

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 with 6 items, user info, logout
  • Header: Dynamic greeting, notifications with dropdown (unread count, 30s polling)
  • MobileNav: Bottom nav with 5 items (lg:hidden)

QA — Quality and Testing

Quality Gates — 10/10 Passing

GateStatusDetail
Type SafetyPassTypeScript strict in server (39 files) and web (28 files). Zod enforces runtime validation.
Input ValidationPassAll 27 endpoints validate inputs via Zod + Fastify validation hooks.
Business RulesPass10/10 BRs implemented (BR-01 to BR-10).
Auth SecurityPassJWT + bcrypt (cost 10). Auth middleware on all protected routes. CORS configured.
Error HandlingPassAppError with ErrorCode enum (15 categories). ZodError transformed. Safe 500 responses.
Design SystemPass100% compliance with design_spec.md. Dark theme, lime accent, Inter font.
API IntegrationPassWeb integrates with all 27 endpoints via centralized fetch wrapper (api.ts).
ResponsivePassDesktop sidebar (≥1024px) + mobile bottom nav. Tested: 375px, 768px, 1024px, 1440px.
Test CoveragePass29 test cases in 4 files. Highest-risk modules: auth (10), pix (11), accounts (4), payments (4).
AccessibilityPass*Semantic HTML, form labels, color contrast AA. *Notes: skip-to-content and aria-labels on icon buttons pending.

Findings (5)

SeverityCategoryDescription
lowTest Coverage5 modules (transactions, cards, users, notifications, dashboard) without dedicated test files. The 4 tested modules cover the highest-risk functionality.
infoAccessibilityMissing skip-to-content link, focus management on route changes, and aria-label on icon buttons.
infoPerformanceFetch wrapper without caching/deduplication. Consider React Query or SWR in a future iteration.
lowSecurityJWT in localStorage (vs. httpOnly cookies). Acceptable for MVP without third-party scripts.
infoObservabilityLogging via console without structured format. Integrate pino (native Fastify) in Phase 04.

Phase 03 HITLs

HITLScopeAgentDecision
#7DDD Architecture (5 BCs, 7 ADRs, ubiquitous language, aggregates)Software ArchitectApproved
#8Backend (9 modules, 27 endpoints, 10/10 BRs, 9 tables, seed)Backend DeveloperApproved
#9Frontend (10 pages, 9 components, design system compliance)Frontend DeveloperApproved
#10QA (29 test cases, 10/10 quality gates, 5 info/low findings)QAApproved

Generated Artifacts

03-product-delivery/architecture/bounded-contexts.json 03-product-delivery/architecture/ubiquitous-language.json 03-product-delivery/architecture/aggregates.json 03-product-delivery/architecture/adrs.json 03-product-delivery/architect-output.json 03-product-delivery/backend-output.json 03-product-delivery/frontend-output.json 03-product-delivery/qa-report.json 03-product-delivery/phase-03-output.json