Fase 03 — Product Delivery
Arquitetura, implementação backend (API + persistência), frontend (UI + integração) e quality gate.
68
Arquivos criados
35
Endpoints API
12
Tabelas SQLite
12
Páginas React
Arquitetura
Stack Tecnológica
| Camada | Tecnologia | Versão | Papel |
|---|---|---|---|
| Backend | Fastify | 5.0 | Servidor HTTP de alta performance |
| TypeScript | 5.5 | Linguagem base (strict mode) | |
| Zod | 3.23 | Validação de inputs e schemas | |
| better-sqlite3 | — | Banco SQLite síncrono | |
| jsonwebtoken + bcryptjs | — | Auth JWT | |
| Frontend | React | 18.3 | UI library |
| TypeScript | 5.6 | Tipagem segura | |
| React Router | 6.26 | Navegação SPA | |
| Tailwind CSS | 3.4 | Estilização utility-first | |
| Vite | 5.4 | Build tool + HMR | |
| Lucide React | — | Ícones SVG | |
| Banco | SQLite3 | — | database-emps.sqlite (WAL mode) |
Bounded Contexts (Domain-Driven Design)
6 Bounded Contexts em monólito modular Fastify. Separação lógica por módulos com JWT compartilhado PF/PJ.
classDiagram
class IdentityPJ {
+AuthPJ (switch PF-PJ)
+RBAC Middleware
+JWT shared com PF
POST /auth/pj/switch
GET /auth/pj/me
}
class CompanyMgmt {
+Company (aggregate root)
+TeamMember
+CNPJ, NaturezaJuridica
POST /companies
GET /companies/me
PATCH /companies/me
}
class BankingPJ {
+PJAccount (aggregate root)
+PJTransaction
+Money, Category
GET /pj/accounts/balance
POST /pj/accounts/transfer-pf
}
class PixPJ {
+PJPixKey (aggregate root)
+PixTransfer
+PixKeyType, RateLimit
POST /pj/pix/transfer
CRUD /pj/pix/keys
}
class Billing {
+Invoice (aggregate root)
+Barcode, PixQRCode
+InvoiceStatus
POST /pj/invoices
GET /pj/invoices
}
class CorporateCards {
+CorporateCard (aggregate root)
+CardPurchase, CardInvoice
+CardLimit
CRUD /pj/corporate-cards
GET /pj/corporate-cards/:id/invoice
}
IdentityPJ --> CompanyMgmt : PJ context
IdentityPJ --> BankingPJ : User authenticated
CompanyMgmt --> BankingPJ : Company owns Account
PixPJ --> BankingPJ : debit/credit
Billing --> BankingPJ : credit on payment
CorporateCards --> BankingPJ : debit on purchase
Diagramas de Sequência
Switch PF → PJ — Autenticação Compartilhada
sequenceDiagram
participant U as Usuário (Browser)
participant F as Frontend (React)
participant A as API Emps (Fastify :3334)
participant DB as SQLite (database-emps)
Note over U,DB: Usuário já logado no Bank PF
U->>F: Clica "Mudar para PJ"
F->>A: POST /api/auth/pj/switch
Note over A: Header: Authorization Bearer (JWT do PF)
A->>A: Valida JWT (shared secret com PF)
A->>DB: SELECT company WHERE owner_user_id = ?
DB-->>A: company { id, cnpj, razao_social }
A->>DB: SELECT team_member WHERE user_id = ? AND company_id = ?
DB-->>A: { role: "admin" }
A->>A: Gera contexto PJ (companyId + role)
A-->>F: { company, role, pjAccount }
F->>F: Ativa modo PJ no ProfileSwitcher
F-->>U: Dashboard PJ carregado
Pix PJ — Transferência Empresarial
sequenceDiagram
participant U as Usuário (Browser)
participant F as Frontend (React)
participant A as API Emps (Fastify)
participant DB as SQLite
U->>F: Informa chave Pix destino
F->>A: GET /api/pj/pix/lookup?key=...
A->>DB: SELECT pix_key WHERE key_value = ?
A-->>F: { name, keyType }
F-->>U: Exibe destinatário
U->>F: Informa valor (ex: R$ 2.000)
F->>A: POST /api/pj/pix/transfer
Note over A: Middleware: JWT + requireRole(financial)
A->>A: Verificar rate limit (20/hora)
A->>DB: SELECT COUNT pj_pix_rate_limit (ultima hora)
DB-->>A: count = 5 (OK)
A->>A: Verificar saldo PJ
A->>DB: SELECT balance FROM pj_accounts
DB-->>A: balance = 850000 (R$ 8.500)
Note over A,DB: Transação atômica (SQLite)
A->>DB: BEGIN TRANSACTION
A->>DB: UPDATE pj_accounts SET balance -= 200000
A->>DB: INSERT pj_transactions (débito, category: pix_out)
A->>DB: INSERT pj_pix_rate_limit
A->>DB: INSERT pj_audit_logs (action: pix_transfer)
A->>DB: COMMIT
A-->>F: { transactionId, status: "completed", balanceAfter }
F-->>U: Comprovante de transferência PJ
Emissão de Boleto de Cobrança
sequenceDiagram
participant U as Usuário (Browser)
participant F as Frontend (React)
participant A as API Emps (Fastify)
participant DB as SQLite
U->>F: Preenche dados da cobrança
Note over F: customer_name, amount, due_date, type
F->>A: POST /api/pj/invoices
Note over A: Middleware: JWT + requireRole(financial)
A->>A: Validar dados (Zod schema)
A->>A: Gerar barcode FEBRABAN (mock)
A->>A: Gerar Pix QR Code (copia-e-cola)
A->>DB: INSERT invoices (status: pending)
DB-->>A: { invoiceId }
A->>DB: INSERT pj_notifications (nova cobrança criada)
A-->>F: { id, barcode, pixQrcode, status: "pending" }
F-->>U: Preview do boleto + QR Code Pix
Dashboard PJ — Carregamento Inicial
sequenceDiagram
participant U as Usuário (Browser)
participant F as Frontend (React)
participant A as API Emps (Fastify)
participant DB as SQLite
U->>F: Acessa /pj/dashboard
F->>F: Verifica JWT + contexto PJ
par Chamadas paralelas
F->>A: GET /api/pj/dashboard
A->>DB: SELECT balance FROM pj_accounts
A->>DB: SELECT SUM(amount) GROUP BY direction (cash flow)
A->>DB: SELECT COUNT invoices BY status (cobranças)
A-->>F: { balance, cashFlow, invoiceSummary }
and
F->>A: GET /api/pj/transactions?limit=5
A->>DB: SELECT * FROM pj_transactions ORDER BY created_at DESC
A-->>F: { transactions: [...] }
and
F->>A: GET /api/pj/notifications/unread-count
A->>DB: SELECT COUNT(*) WHERE is_read = 0
A-->>F: { count: 7 }
end
F->>F: Renderiza dashboard PJ completo
F-->>U: Saldo + Cash Flow + Cobranças + Transações + Notificações
Architectural Decision Records (ADRs)
ADR-001Accepted
SQLite como banco de dados PJ separado
Decisão: Usar database-emps.sqlite separado do database.sqlite (PF), no mesmo servidor, com WAL mode habilitado.
Positivo: Isolamento de dados PF/PJ, backup independente, zero infra adicional, consistente com ecossistema ECP.
Negativo: SQLite single-writer pode causar contenção em escala. Aceitável para MVP (500 empresas).
ADR-002Accepted
JWT compartilhado entre PF e PJ
Decisão: Compartilhar JWT_SECRET entre ecp-digital-bank e ecp-digital-bank-emps. O auth-pj module valida tokens do PF e adiciona contexto PJ (companyId, role).
Positivo: SSO nativo, zero fricção no toggle PF↔PJ, sem novo login.
Negativo: Compromisso de uma chave compromete ambos. Aceitável para MVP; evoluir para auth service em produção.
ADR-003Accepted
RBAC com 3 perfis hierárquicos
Decisão: Admin > Financial > Viewer. Middleware requireRole() valida hierarquia por rota. Toda ação registra userId do operador.
Positivo: Granularidade suficiente para MVP, auditoria completa, simples de entender.
Negativo: Sem permissões customizáveis por recurso. Suficiente para 3 perfis; expandir se necessário.
ADR-004Accepted
Boleto com barcode mock FEBRABAN
Decisão: Gerar barcode e linha digitável com estrutura válida mas sem registro real em CIP (registradora). Mock para MVP.
Positivo: Permite desenvolvimento completo do fluxo de cobrança sem dependência externa.
Negativo: Boletos não são pagáveis em outros bancos. Integração real necessária para produção.
ADR-005Accepted
Valores monetários em centavos (integer)
Decisão: Todo valor financeiro armazenado como INTEGER em centavos. Frontend converte para exibição. Utilities money.ts para conversão.
Positivo: Zero erros de arredondamento, precisão absoluta, padrão da indústria fintech.
Negativo: Requer conversão em toda exibição. Custo mínimo vs. benefício enorme.
Modelo de Dados (12 tabelas + 22 índices)
companies
Cadastro de empresas (MEI, EI, EIRELI, LTDA, SLU)
id, owner_user_id, cnpj, razao_social, nome_fantasia, natureza_juridica, endereco, status, created_at, updated_at, deleted_at
pj_accounts
Contas PJ com saldo em centavos
id, company_id, agency(0001), number, balance, daily_transfer_limit, status
team_members
Multi-usuários com RBAC
id, company_id, user_id, role(admin|financial|viewer), status(active|invited|removed)
pj_transactions
Transações PJ com categorização
id, account_id, operator_id, type, category, amount, balance_after, direction, reference_id(idempotency)
pj_pix_keys
Chaves Pix PJ (máx 20)
id, company_id, account_id, type(cnpj|email|phone|random), value, status
invoices
Boletos emitidos (cobranças)
id, company_id, customer_name, amount, due_date, barcode, pix_qrcode, status(pending|paid|overdue|cancelled), type(single|installment|recurring)
corporate_cards
Cartões corporativos virtuais
id, company_id, holder_id, last4, limit_cents, used_cents, due_day, status
corporate_card_purchases
Compras no cartão corporativo
id, card_id, merchant_name, merchant_category, amount, status
corporate_invoices
Faturas do cartão
id, card_id, reference_month, total_cents, due_date, status(open|closed|paid|overdue)
pj_notifications
Notificações da empresa
id, company_id, user_id, title, body, type, is_read
pj_pix_rate_limit
Rate limiting Pix (20/hora)
id, account_id, window_start, transfer_count
pj_audit_logs
Audit trail completo
id, company_id, user_id, action, resource, resource_id, metadata, ip_address
Diagrama Entidade-Relacionamento
12 tabelas, 22 índices. SQLite3 com WAL mode e foreign keys habilitadas. Valores monetários em centavos (INTEGER).
erDiagram
companies {
TEXT id PK
TEXT owner_user_id FK
TEXT cnpj UK
TEXT razao_social
TEXT nome_fantasia
TEXT natureza_juridica
TEXT endereco
TEXT status
TEXT created_at
}
pj_accounts {
TEXT id PK
TEXT company_id FK "UK"
TEXT agency
TEXT number UK
INTEGER balance
INTEGER daily_transfer_limit
TEXT status
}
team_members {
TEXT id PK
TEXT company_id FK
TEXT user_id FK
TEXT role "admin|financial|viewer"
TEXT status "active|invited|removed"
}
pj_transactions {
TEXT id PK
TEXT account_id FK
TEXT operator_id FK
TEXT type
TEXT category
INTEGER amount
INTEGER balance_after
TEXT direction "in|out"
TEXT reference_id UK
}
pj_pix_keys {
TEXT id PK
TEXT company_id FK
TEXT account_id FK
TEXT type "cnpj|email|phone|random"
TEXT value UK
TEXT status
}
invoices {
TEXT id PK
TEXT company_id FK
TEXT customer_name
INTEGER amount
TEXT due_date
TEXT barcode
TEXT pix_qrcode
TEXT status "pending|paid|overdue|cancelled"
TEXT type "single|installment|recurring"
}
corporate_cards {
TEXT id PK
TEXT company_id FK
TEXT holder_id FK
TEXT last4
INTEGER limit_cents
INTEGER used_cents
INTEGER due_day
TEXT status
}
corporate_card_purchases {
TEXT id PK
TEXT card_id FK
TEXT merchant_name
TEXT merchant_category
INTEGER amount
TEXT status
}
corporate_invoices {
TEXT id PK
TEXT card_id FK
TEXT reference_month
INTEGER total_cents
TEXT due_date
TEXT status "open|closed|paid|overdue"
}
pj_notifications {
TEXT id PK
TEXT company_id FK
TEXT user_id FK
TEXT title
TEXT body
TEXT type
INTEGER is_read
}
pj_audit_logs {
TEXT id PK
TEXT company_id FK
TEXT user_id FK
TEXT action
TEXT resource
TEXT resource_id
TEXT metadata
TEXT ip_address
}
companies ||--|| pj_accounts : "has"
companies ||--o{ team_members : "has"
companies ||--o{ pj_pix_keys : "registers"
companies ||--o{ invoices : "issues"
companies ||--o{ corporate_cards : "owns"
companies ||--o{ pj_notifications : "receives"
companies ||--o{ pj_audit_logs : "logs"
pj_accounts ||--o{ pj_transactions : "records"
corporate_cards ||--o{ corporate_card_purchases : "has"
corporate_cards ||--o{ corporate_invoices : "generates"
Módulos da API (35 endpoints)
auth-pj
Proxy auth + switch PF→PJ + RBAC
2 endpoints: POST /auth/pj/switch, GET /auth/pj/me
auth-pj.routes.ts · auth-pj.service.ts · auth-pj.schema.ts
companies
Cadastro e gestão de empresas
3 endpoints: POST, GET /me, PATCH /me
companies.routes.ts · companies.service.ts · companies.schema.ts
pj-accounts
Conta PJ + saldo + transfer PF↔PJ
3 endpoints: GET /me, GET /balance, POST /transfer-pf
pj-accounts.routes.ts · pj-accounts.service.ts · pj-accounts.schema.ts
pj-pix
Pix empresarial completo
6 endpoints: transfer, keys (CRUD), qrcode, lookup
pj-pix.routes.ts · pj-pix.service.ts · pj-pix.schema.ts
invoices
Boletos de cobrança
6 endpoints: create, list, detail, cancel, resend, summary
invoices.routes.ts · invoices.service.ts · invoices.schema.ts
pj-transactions
Extrato PJ + categorização
3 endpoints: list (cursor), detail, summary
pj-transactions.routes.ts · pj-transactions.service.ts · pj-transactions.schema.ts
corporate-cards
Cartões corporativos + faturas
7 endpoints: CRUD, limit, block, invoice, purchases
corporate-cards.routes.ts · corporate-cards.service.ts · corporate-cards.schema.ts
team
Multi-usuários + convites + RBAC
4 endpoints: invite, list, change role, remove
team.routes.ts · team.service.ts · team.schema.ts
pj-notifications
Notificações da empresa
4 endpoints: list, unread-count, mark-read, read-all
pj-notifications.routes.ts · pj-notifications.service.ts · pj-notifications.schema.ts
pj-dashboard
Dados agregados do dashboard
1 endpoint: GET /pj/dashboard
pj-dashboard.routes.ts · pj-dashboard.service.ts
Páginas React (12 telas)
/pj/dashboard
Dashboard PJ
Saldo, cash flow, cobranças, ações rápidas
/pj/extrato
Extrato PJ
Transações com categorias + filtros
/pj/pix/enviar
Pix Enviar
Wizard 4 passos + limites
/pj/pix/receber
Pix Receber
QR Code + copia e cola
/pj/pix/chaves
Pix Chaves
CRUD de chaves (máx 20)
/pj/cobrancas/nova
Nova Cobrança
Form 2 etapas + preview boleto
/pj/cobrancas
Lista de Cobranças
Tabela + badges + filtros
/pj/cobrancas/:id
Detalhe Cobrança
Timeline + barcode + ações
/pj/cartoes
Cartões Corporativos
Visual card + limit bar
/pj/cartoes/:id/fatura
Fatura do Cartão
Compras + total + vencimento
/pj/time
Gestão do Time
Membros + convite + roles
/pj/empresa
Dados da Empresa
Cadastro + edição (admin)
Estrutura de Pastas
ecp-digital-emps/
├── server/
│ ├── src/
│ │ ├── app.ts ← Fastify + plugins + routes
│ │ ├── server.ts ← Entry point (port 3334)
│ │ ├── database/
│ │ │ ├── connection.ts ← SQLite3 + WAL mode
│ │ │ ├── migrations/ ← 001-initial.sql (12 tabelas)
│ │ │ └── seed.ts ← Dados demo AB Design Studio
│ │ ├── modules/ ← 10 módulos (30 arquivos)
│ │ │ ├── auth-pj/ companies/ pj-accounts/
│ │ │ ├── pj-pix/ invoices/ pj-transactions/
│ │ │ ├── corporate-cards/ team/
│ │ │ ├── pj-notifications/ pj-dashboard/
│ │ ├── shared/
│ │ │ ├── errors/ middleware/ utils/
│ │ └── types/
│ └── package.json
├── web/
│ ├── src/
│ │ ├── App.tsx ← Layout + Router
│ │ ├── routes/ ← 12 páginas PJ
│ │ ├── components/
│ │ │ ├── ui/ ← Button, Card, Input, Modal, Badge
│ │ │ └── layout/ ← SidebarPJ, HeaderPJ, ProfileSwitcher
│ │ ├── hooks/ services/ lib/ styles/
│ └── package.json
├── package.json ← Root (npm run dev)
└── .env.example
HITLs #7 a #10 — Decisão de Aprovação da Fase 03
#7: Arquitetura sólida? Contratos prontos? •
#8: APIs corretas? Persistência validada? •
#9: Front integrado? Design fiel ao design_spec? •
#10: Qualidade aprovada? Pronto para operar?