Fase 03 — Product Delivery
Arquitetura DDD, ADRs, API REST, banco de dados, stack técnica e QA report.
Diagrama de Bounded Contexts (DDD)
Mapa visual dos 6 bounded contexts e suas dependências no domínio FoodFlow.
BOUNDED CONTEXT MAP — FOODFLOW
ECP Digital Bank
(Sistema Externo)
PIX Transfer, QR Code,
Webhooks, Virtual Cards
Identity
Auth, sessoes e perfis
Aggregates: User, Address, Session
JWT 24h + Refresh 7d + bcrypt
Roles: consumer, restaurant, admin
Catalog
Restaurantes, cardapios e categorias
Aggregates: Restaurant, MenuItem, Category
Dominio publico (sem auth)
6 restaurantes, 24 itens, 7 categorias
Ordering
Carrinho, pedidos e cupons
Aggregates: Cart, Order, Coupon
pending → confirmed → preparing
Frete gratis >= R$ 120
Payment
Cartao ECP, PIX QR, webhooks, SSE
Aggregates: Payment, BankTransaction, PixQrCode
Circuit breaker (5 falhas = 30s)
Valores em centavos | HMAC-SHA256
Delivery
Tracking simulado de entregas
Aggregates: DeliveryTracking
confirmed → preparing → delivered
MVP: sem GPS, ETA via eta_min/max
Admin
Dashboard e gestao da plataforma
Sub: PlatformAdmin, RestaurantAdmin
CRUD restaurantes, categorias, cupons
Metricas: pedidos, receita, ticket medio
user_id
auth token
menu_item_id
order_id
status update
API calls
webhook PIX
order status
CRUD
metricas + cupons
LEGENDA
Dependencia direta
Referencia / Admin
Integracao externa
Sistema externo
Bounded Context
Arquitetura — 6 Bounded Contexts
Identity
Autenticação, autorização, sessões e perfis
Aggregates: User (User, Address, Session)
JWT 24h + Refresh Token 7d. Roles: consumer, restaurant, admin. bcryptjs 12 rounds.
Catalog
Restaurantes, categorias, cardápios e itens
Aggregates: Restaurant, Category
Domínio público (leitura sem auth). 6 restaurantes seed, 24 itens, 7 categorias.
Ordering
Carrinho, pedidos, itens de pedido, cupons
Aggregates: Cart, Order, Coupon
Status: pending_payment → confirmed → preparing → out_for_delivery → delivered. Frete grátis ≥ R$ 120.
Payment
Integração ECP Digital Bank: cartão virtual, PIX, webhooks, SSE
Aggregates: Payment + Services (bank-integration, webhook-handler, sse-manager)
Valores em centavos. Circuit breaker (5 falhas = 30s block). PIX expira em 10 min. HMAC-SHA256 validation.
Delivery
Tracking simulado de entregas
Referência cruzada com Ordering context
MVP: restaurante atualiza status manualmente. Sem GPS real. ETA calculado a partir de eta_min/eta_max.
Admin
Dashboard, gestão de restaurantes, categorias, cupons
Aggregates: PlatformAdmin, RestaurantAdmin
Métricas: total restaurantes, pedidos, receita, ticket médio. CRUD de restaurantes, categorias e cupons.
Architecture Decision Records (ADRs)
ADR-001
Monorepo Single-Server Architecture
Accepted
Decisão: Fastify serve API REST (/api/*) + React SPA (client/dist/) em um único processo Node.js. Em dev, Vite com HMR separado; em produção, static files.
Positivo: Deploy simplificado (PM2), custo mínimo (VPS R$ 20-50/mês), padrão validado no ecp-digital-banking.
Negativo: Escalabilidade horizontal limitada, frontend e backend acoplados no deploy.
ADR-002
SQLite with better-sqlite3 as Primary Database
Accepted
Decisão: SQLite via better-sqlite3 11.x com WAL mode. Arquivo em ./data/foodflow.db. Migrations inline no startup.
Positivo: Zero-config, performance excepcional para < 100k rows, prepared statements nativos, API síncrona.
Negativo: Escritas concorrentes limitadas, sem replicação nativa.
ADR-003
ECP Digital Bank Integration as Payment Proxy
Accepted
Decisão: FoodFlow atua como proxy de pagamento. bank-integration.mjs encapsula todas as chamadas. Cartão Virtual ECP via débito PIX transfer + PIX QR Code com webhook + SSE.
Positivo: Pagamentos reais sem gateway externo, segurança server-side, JWT efêmero.
Negativo: Dependência de disponibilidade do banco, latência de proxy.
ADR-004
SSE for Real-Time PIX Payment Notification
Accepted
Decisão: Server-Sent Events via GET /api/payments/:id/events. SSE Manager com Map<paymentId, Set<ServerResponse>>. Auth via token JWT curta duração (5 min) em query param.
Positivo: Nativo no browser (EventSource), unidirecional, baixo overhead, auto-reconnect.
Negativo: EventSource não suporta headers customizados, limite 6 conexões em HTTP/1.1.
ADR-005
CSS Modules with CSS Custom Properties for Theming
Accepted
Decisão: CSS Modules (*.module.css) para escopo isolado + CSS Custom Properties (variáveis CSS) para theming global Midnight Express.
Positivo: Zero runtime JS, theming centralizado, sem conflito de estilos, bundle mínimo.
Negativo: Sem dynamic styling baseado em props (resolvido com style prop inline).
Diagrama de Sequência — Fluxo Completo de Pedido + Pagamento
Fluxo end-to-end: do pedido ao pagamento (Cartão ECP e PIX QR Code) com integração ao ECP Digital Bank.
DIAGRAMA DE SEQUENCIA — PEDIDO + PAGAMENTO (PIX & CARTAO ECP)
Consumer
(React SPA)
FoodFlow API
(Fastify Server)
ECP Digital Bank
(API Externa)
Webhook Handler
(HMAC Validator)
SSE Manager
(Real-time)
FASE: PEDIDO
1. GET /api/restaurants/:id/menu
2. { menuItems[] }
3. POST /api/cart/items + POST /api/orders
4. { order_id, total, status: pending_payment }
FLUXO A: PAGAMENTO CARTAO VIRTUAL ECP
5a. POST /api/payments/bank-auth { email, senha }
6a. bankLogin(email, password)
7a. { ephemeral bankJWT, cards[] }
8a. POST /api/payments/card { order_id, card_id }
9a. bankPixTransfer(bankJwt, amountCents)
10a. { transactionId, status: completed }
11a. Pagamento OK, order confirmed
FLUXO B: PAGAMENTO PIX QR CODE (COM WEBHOOK + SSE)
5b. POST /api/payments/pix { order_id }
6b. bankGeneratePixQrCode(platformJwt, amountCents)
7b. { qrcodeData, qrcodeImage, expiresAt (10min) }
8b. { paymentId, qrCodeImage, expiresAt }
Exibe QR + timer
9b. GET /api/payments/:id/events (SSE EventSource)
Registra conexao
Paga no app banco
(escaneia QR)
10b. POST /webhooks/bank/pix-received
{ event: pix.received, X-Webhook-Signature: HMAC }
Valida HMAC-SHA256
Extrai payment_id
11b. payment.status = completed, order.status = confirmed
12b. Emite payment_update
13b. SSE event: { type: payment_update, status: completed } (< 2s SLO)
Sucesso! Redirect
Tela de tracking
LEGENDA
Request
Response
Bank API
Webhook
SSE
Sucesso
Regras de Resiliência do Fluxo
Idempotente: mesmo payment.id reenviado não duplica transação
Timeout banco: 10s com 1x retry para erros 5xx (backoff 2s)
Circuit breaker: 5 falhas consecutivas = 30s de bloqueio
PIX expira: 10 minutos — worker verifica a cada 30s
Webhook: sempre retorna 200 OK para prevenir retries do banco
SSE auth: token JWT curta duração (5 min) via query param
Bank JWT efêmero: nunca persistido além da transação, limpo após conclusão
API REST — 48 Endpoints
Authentication (3)
POST /api/auth/register
POST /api/auth/login
POST /api/auth/refresh
Consumer Profile (5)
GET /api/consumer/profile
PUT /api/consumer/profile
GET /api/consumer/addresses
POST /api/consumer/addresses
DELETE /api/consumer/addresses/:id
Catalog (4)
GET /api/categories
GET /api/restaurants
GET /api/restaurants/:id
GET /api/restaurants/:id/menu
Cart (5)
GET /api/cart
POST /api/cart/items
PUT /api/cart/items/:id
DELETE /api/cart/items/:id
DELETE /api/cart
Orders (4)
POST /api/orders
GET /api/orders
GET /api/orders/:id
PATCH /api/orders/:id/status
Coupons (1)
POST /api/coupons/validate
Payments (5)
POST /api/payments/bank-auth
GET /api/payments/bank-cards
GET /api/payments/bank-balance
POST /api/payments/card
POST /api/payments/pix
GET /api/payments/:id/events SSE
Webhook (1)
POST /api/webhooks/bank/pix-received
Favorites (3)
GET /api/favorites
POST /api/favorites
DELETE /api/favorites/:restaurantId
Restaurant Admin (7)
GET /api/restaurant-admin/profile
POST /api/restaurant-admin/menu-items
PUT /api/restaurant-admin/menu-items/:id
DELETE /api/restaurant-admin/menu-items/:id
GET /api/restaurant-admin/orders
PUT /api/restaurant-admin/orders/:id/status
PUT /api/restaurant-admin/settings
Platform Admin (9)
GET /api/admin/dashboard
GET /api/admin/restaurants
PATCH /api/admin/restaurants/:id/status
GET /api/admin/categories
POST /api/admin/categories
PUT /api/admin/categories/:id
DELETE /api/admin/categories/:id
GET /api/admin/coupons
POST /api/admin/coupons
PUT /api/admin/coupons/:id
Diagrama de Entidade-Relacionamento (ER)
Modelo de dados completo com 12 tabelas, chaves primárias, estrangeiras e cardinalidades (14 FKs).
ENTITY-RELATIONSHIP DIAGRAM — SQLITE (better-sqlite3 11.x)
users
PK id INTEGER
email TEXT UQ
password_hash TEXT
name, phone TEXT
role TEXT
FK restaurant_id → restaurants
created_at, updated_at
addresses
PK id INTEGER
FK user_id → users CASCADE
label, street, number
city, state, zip_code
is_default INTEGER
categories
PK id INTEGER
name TEXT UQ
emoji TEXT
sort_order, is_active INTEGER
restaurants
PK id INTEGER
name, slug TEXT (slug UQ)
FK category_id → categories
cuisine, subtitle, rating
eta_min, eta_max INTEGER
delivery_fee, min_order REAL
cover_gradient, hero_emoji
is_active, is_open INTEGER
menu_items
PK id INTEGER
FK restaurant_id → restaurants
name, description TEXT
price REAL
emoji, badge, category TEXT
sort_order, is_available INTEGER
carts
PK id INTEGER
FK user_id → users UQ
updated_at TEXT
cart_items
PK id INTEGER
FK cart_id → carts CASCADE
FK menu_item_id → menu_items
quantity INTEGER > 0
orders
PK id INTEGER
FK user_id → users
address_text TEXT
subtotal, delivery_fee REAL
discount, total REAL
coupon_code TEXT
payment_method TEXT
status TEXT (6 states)
order_items
PK id INTEGER
FK order_id → orders CASCADE
FK restaurant_id → restaurants
restaurant_name TEXT (snap)
item_name, item_price TEXT/REAL
quantity INTEGER
favorites
PK id INTEGER
FK user_id → users CASCADE
FK restaurant_id → restaurants
UNIQUE(user_id, restaurant_id)
coupons
PK id INTEGER
code TEXT UQ
discount_type TEXT (fixed|percent)
discount_value, min_order REAL
max_uses, uses_count INTEGER
is_active, expires_at INT/TEXT
payments
PK id TEXT (p_XXXX)
FK order_id → orders UQ
FK user_id → users
method TEXT (card|pix)
status TEXT (5 states)
amount_cents INTEGER
bank_transaction_id TEXT
pix_qrcode_data TEXT
card_last4, pix_expiration
1:N
1:1
1:N (user_id)
1:N
1:N
1:N
1:N
1:N (restaurant_id)
1:N
N:1
1:N
1:1 (payments)
1:N (user_id)
LEGENDA
PK Primary Key
FK Foreign Key
Relacao direta
FK reference
UQ = UNIQUE | CASCADE = ON DELETE
14 Foreign Keys | WAL mode
SQLite via better-sqlite3 11.x | WAL mode | Seed: 7 categorias, 6 restaurantes, 24 itens, cupom MVP10 (R$ 10 off, min R$ 80), 3 users demo
Banco de Dados — 12 Tabelas (Resumo)
Tabela Descrição Colunas
users Usuários (consumer, restaurant, admin) id, email, password_hash, name, phone, role, restaurant_id, created_at, updated_at
addresses Endereços de entrega id, user_id, label, street, number, complement, neighborhood, city, state, zip_code, is_default
categories Categorias do marketplace id, name, emoji, sort_order, is_active
restaurants Restaurantes parceiros id, name, slug, cuisine, subtitle, category_id, rating, review_count, eta_min, eta_max, delivery_fee, min_order, cover_gradient, hero_emoji, promo_text, tags, is_active, is_open
menu_items Itens do cardápio id, restaurant_id, name, description, price, emoji, badge, category, sort_order, is_available
carts Carrinho server-side (1 por usuário) id, user_id, updated_at
cart_items Itens no carrinho id, cart_id, menu_item_id, quantity
orders Pedidos com valores calculados id, user_id, address_text, subtotal, delivery_fee, discount, total, coupon_code, payment_method, status
order_items Snapshot de itens do pedido id, order_id, restaurant_id, restaurant_name, menu_item_id, item_name, item_price, quantity
favorites Relação consumer-restaurante id, user_id, restaurant_id, created_at
coupons Cupons de desconto id, code, discount_type, discount_value, min_order, max_uses, uses_count, is_active, expires_at
payments Pagamentos integrados com ECP Bank id, order_id, user_id, method, status, amount_cents, bank_transaction_id, pix_qrcode_data, pix_expiration, card_last4, webhook_received_at
Engine: SQLite via better-sqlite3 11.x | Seed: 7 categorias, 6 restaurantes, 24 itens, cupom MVP10 (R$ 10 off, min R$ 80), 3 usuários demo.
Stack Técnica
Backend
Runtime: Node.js 20 LTS
Framework: Fastify 5.x
Database: SQLite via better-sqlite3 11.x
Auth: JWT (jsonwebtoken) + bcryptjs
Validation: TypeBox (JSON Schema)
SSE: reply.raw.write nativo
Webhook: HMAC-SHA256 + timingSafeEqual
Frontend
Framework: React 18
Build: Vite 5.x
Styling: CSS Modules + CSS Custom Properties
Routing: React Router 6
State: Context API (Auth, Cart, Theme)
HTTP: Fetch API com wrapper custom
SSE: EventSource nativo + hook useSSE
QA Report
Bugs Críticos
BUG-001 — Critical
EventSource nativo não suporta headers customizados (Authorization: Bearer)
Sem autenticação na SSE, qualquer pessoa pode escutar eventos de pagamento. Solução: token via query param com expiração curta (5 min).
BUG-002 — Critical
Regra de frete grátis não considera corretamente carrinho multi-restaurante
Frete de todos os restaurantes no carrinho precisa ser somado antes de aplicar isenção (≥ R$ 120). Cálculo agregado não é trivial.
Bugs Major
BUG-003 — Major
Inconsistência cupom MVP10: 10% no backlog vs R$ 10 fixo no tech_spec
BUG-004 — Major
Sem validação de pedido mínimo por restaurante no checkout
Condições para Deploy
Corrigir BUG-001: autenticação SSE via query param token
Corrigir BUG-002: regra de frete multi-restaurante
Resolver BUG-003: decidir se MVP10 é 10% ou R$ 10
Implementar BUG-004: validação min_order
Smoke tests P0 (32 casos) passando 100%
Core tests P1 (68 casos) passando ≥ 95%
Zero bugs críticos abertos
Artefatos Gerados
03-product-delivery/architecture.json
03-product-delivery/qa-report.json