Fase 03 — Product Delivery
HITLs #7-#10 Aprovados Architect + Dev + QA

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 PKidINTEGER emailTEXT UQ password_hashTEXT name, phoneTEXT roleTEXT FKrestaurant_id→ restaurants created_at, updated_at addresses PKidINTEGER FKuser_id→ users CASCADE label, street, number city, state, zip_code is_defaultINTEGER categories PKidINTEGER nameTEXT UQ emojiTEXT sort_order, is_activeINTEGER restaurants PKidINTEGER name, slugTEXT (slug UQ) FKcategory_id→ categories cuisine, subtitle, rating eta_min, eta_maxINTEGER delivery_fee, min_orderREAL cover_gradient, hero_emoji is_active, is_openINTEGER menu_items PKidINTEGER FKrestaurant_id→ restaurants name, descriptionTEXT priceREAL emoji, badge, categoryTEXT sort_order, is_availableINTEGER carts PKidINTEGER FKuser_id→ users UQ updated_atTEXT cart_items PKidINTEGER FKcart_id→ carts CASCADE FKmenu_item_id→ menu_items quantityINTEGER > 0 orders PKidINTEGER FKuser_id→ users address_textTEXT subtotal, delivery_feeREAL discount, totalREAL coupon_codeTEXT payment_methodTEXT statusTEXT (6 states) order_items PKidINTEGER FKorder_id→ orders CASCADE FKrestaurant_id→ restaurants restaurant_nameTEXT (snap) item_name, item_priceTEXT/REAL quantityINTEGER favorites PKidINTEGER FKuser_id→ users CASCADE FKrestaurant_id→ restaurants UNIQUE(user_id, restaurant_id) coupons PKidINTEGER codeTEXT UQ discount_typeTEXT (fixed|percent) discount_value, min_orderREAL max_uses, uses_countINTEGER is_active, expires_atINT/TEXT payments PKidTEXT (p_XXXX) FKorder_id→ orders UQ FKuser_id→ users methodTEXT (card|pix) statusTEXT (5 states) amount_centsINTEGER bank_transaction_idTEXT pix_qrcode_dataTEXT 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 PKPrimary Key FKForeign 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)

TabelaDescriçãoColunas
usersUsuários (consumer, restaurant, admin)id, email, password_hash, name, phone, role, restaurant_id, created_at, updated_at
addressesEndereços de entregaid, user_id, label, street, number, complement, neighborhood, city, state, zip_code, is_default
categoriesCategorias do marketplaceid, name, emoji, sort_order, is_active
restaurantsRestaurantes parceirosid, 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_itemsItens do cardápioid, restaurant_id, name, description, price, emoji, badge, category, sort_order, is_available
cartsCarrinho server-side (1 por usuário)id, user_id, updated_at
cart_itemsItens no carrinhoid, cart_id, menu_item_id, quantity
ordersPedidos com valores calculadosid, user_id, address_text, subtotal, delivery_fee, discount, total, coupon_code, payment_method, status
order_itemsSnapshot de itens do pedidoid, order_id, restaurant_id, restaurant_name, menu_item_id, item_name, item_price, quantity
favoritesRelação consumer-restauranteid, user_id, restaurant_id, created_at
couponsCupons de descontoid, code, discount_type, discount_value, min_order, max_uses, uses_count, is_active, expires_at
paymentsPagamentos integrados com ECP Bankid, 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

82
Quality Score
142
Test Cases
98
ACs Cobertos
6
Bugs Encontrados
Conditional Go
Go/No-Go

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

Artefatos Gerados

03-product-delivery/architecture.json 03-product-delivery/qa-report.json