Files
sar/design-artifacts/A-Product-Brief/04-platform-requirements.md
julian 17c08e6392 chore: initial monorepo scaffold + WDS Phase 1+2 artifacts
- Nx 22.7 monorepo (pnpm 11.1, TypeScript 5.9, Node 24)
- apps/api: NestJS 11 (CJS conforme CODING-RULES.md PGD-DB-004)
- apps/web: React 19 + Vite 8 (ESM)
- libs/shared/api-interface: Zod contract base
- Docker Compose dev: Postgres 18, Valkey 8, MinIO, Mailpit
- WDS artifacts:
  - design-artifacts/A-Product-Brief/ (5 docs canônicos + 16 dialogs)
  - design-artifacts/B-Trigger-Map/ (hub + 4 personas + feature impact)
- Stack canon: STACK.md v2.2 + CODING-RULES.md v2.0 + brand.md
- AGENTS.md + README.md como entrada para devs/agentes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:34:20 +00:00

348 lines
20 KiB
Markdown

# Platform Requirements: SAR — Força de Vendas
**Status:** ✅ Block D COMPLETO (Steps 27-32) — fast-track via STACK.md v2.2
**Cliente:** JCS Sistemas
**Última atualização:** 2026-05-27
> Este documento é um **espelho consolidado** de decisões já feitas em `STACK.md v2.2`, `CODING-RULES.md v2.0`, ADRs 0001-0006, e Step 10a (Platform Strategy). Não duplica conteúdo — referencia. **STACK.md continua sendo source of truth para qualquer decisão técnica.**
---
## Stakeholder técnico
| Item | Valor |
|---|---|
| **Tech Lead** | Julian (acumulado com PO + Champion — solo founder mode até 1º cliente) |
| **Nível técnico do PO** | Muito técnico (autor de STACK.md + CODING-RULES.md) |
| **Time de desenvolvimento MVP** | Apenas Julian até 1º cliente; +1-2 devs depois |
| **Cultura técnica JCS** | Tech maturity alta — 11-50 pessoas, anos de software profissional |
---
## Decisões já feitas (referência cruzada)
| Decisão | Onde está consolidada |
|---|---|
| **Stack canônica completa** | `STACK.md v2.2` (FONTE DA VERDADE) |
| **Invariantes de código e pegadinhas críticas** | `CODING-RULES.md v2.0` |
| **Multi-tenancy BD-por-workspace** | ADR 0006 (substituiu row-level tenantId) |
| **master-login como IdP** | ADR 0005 (substituiu Keycloak) |
| **Migração AWS sa-east-1 → Proxmox on-prem BR** | ADR 0004 |
| **SLO defaults por endpoint/job/fluxo** | ADR 0001 |
| **Tags Nx canônicas (scope / type / domain)** | ADR 0002 |
| **PWA mobile-first iOS para Rafael** | Step 10a |
| **Desktop-first para Sandra/Daniel/Alice** | Step 10a + brand.md |
| **iPad first-class para Daniel** | Step 10a |
| **Offline read+write com sync (Rafael)** | Step 10a |
| **Coexistência com app Android legado** | Step 10a |
| **Sem app store no MVP** | Step 10a + STACK.md §18 |
| **pt-BR only no MVP, i18n-ready** | Step 16 |
| **LGPD by design** | STACK.md §22 + CODING-RULES.md |
| **brand.md** | identidade visual canônica |
| **`03-visual-direction.md`** | direção visual completa |
---
## Tech Stack (Step 28 — espelho consolidado de STACK.md v2.2)
### Camadas principais
| Camada | Decisão canônica |
|---|---|
| **Runtime** | Node 24 LTS · pnpm 11.1 · TypeScript 5.9 |
| **Monorepo** | Nx 22.7 (`apps/api` + `apps/api-worker` + `apps/web` + `apps/web-e2e` + `libs/`) com 3 tags canônicas (scope / type / domain) — ADR 0002 |
| **Backend** | NestJS 11.1 (Express 5) · Prisma 7 · PostgreSQL 18 · `nestjs-cls` · `@nestjs/terminus` · `@nestjs/throttler` + Valkey storage |
| **Frontend** | React 19.2 + Compiler · Vite 8 (Rolldown) · Ant Design 6.4 · TanStack Query 5.100 · TanStack Router · Zustand 5 |
| **API** | REST + OpenAPI 3.1 + RFC 9457 Problem Details · Zod 4 (pnpm catalog) + nestjs-zod + react-hook-form (`zodResolver`) |
| **Auth** | Guards NestJS + `jose` + **master-login** (IdP OAuth2/OIDC próprio em VM Proxmox; HS256→RS256 roadmap) · argon2id no IdP — ADR 0005 |
| **Multi-tenancy** | **BD-por-workspace** — cluster Postgres dedicado por cliente, resolvido via `get_workspace_connection` no master-login. Sem coluna `tenantId` em modelos — isolamento físico — ADR 0006 |
| **Secrets** | HashiCorp Vault (KV v2) self-host · Vault Agent injeta env vars no entrypoint do container |
| **Observabilidade** | Pino + nestjs-pino 10.3 · OpenTelemetry (Traces/Metrics GA, Logs Beta) · Grafana Cloud LGTM · Sentry |
| **Feature flags** | OpenFeature SDK + GrowthBook self-host |
| **Filas** | BullMQ 5.77 + `@nestjs/bullmq` 11 · Bull Board · Valkey em VM/LXC Proxmox |
| **Cache** | `@nestjs/cache-manager` v3 + cache-manager 6 + `@keyv/redis` + `cacheable` (L1) |
| **Real-time** | Socket.IO 4 + `@socket.io/redis-adapter` (Valkey) · SSE via `@Sse()` nativo NestJS |
| **Uploads** | MinIO (S3-compat) presigned POST policy · AWS SDK v3 · Uppy + `@uppy/aws-s3` · ClamAV worker BullMQ · sharp 0.34 |
| **Email** | Resend (SaaS — únicas exceções SaaS são email e CDN por questão de deliverability/borda) + React Email via BullMQ |
| **Testes** | Vitest 4.1 · Testcontainers 12 · Supertest 7.2.2 (encapsulado) · Playwright 1.60 |
| **Qualidade** | ESLint 10.4 flat · typescript-eslint 8.59 · Prettier 3.8.3 · eslint-plugin-importx 4.16.2 · Husky 9 + lint-staged 17 · gitleaks |
| **CI/Deploy** | GitHub Actions + Nx Cloud · Trivy gate · pin actions por SHA · Docker multi-stage `node:24-alpine` · **Ansible/SSH para VMs Proxmox** com docker-compose rolling (backend) · **Cloudflare + Nginx** servindo SPA estática (frontend) |
| **Infraestrutura** | **Proxmox on-prem BR** — substitui AWS sa-east-1 — ADR 0004. Self-host Postgres, Valkey, MinIO, master-login, Vault, GrowthBook, PostHog (novo) |
### Decisões frontend específicas do SAR (decorrentes de Block C)
- **PWA mobile-first iOS** para Rafael — Service Worker + Web Push + manifest + offline read+write
- **Desktop SPA** para Sandra/Daniel/Alice (mesmo bundle React, layouts diferentes)
- **iPad first-class** para Daniel (testes regulares em iPad Safari)
- **Code-splitting por cockpit** (TanStack Router lazy routes)
- **Service Worker** crítico para Rafael offline (IndexedDB queue + sync)
- **Plus Jakarta Sans self-host** (não Google Fonts CDN — LGPD + performance)
- **Dark mode** elegante (especialmente Rafael à noite)
- **Build target**: ES2023 (`noUncheckedIndexedAccess` ON, NodeNext module resolution)
### Rationale (curto)
A stack JCS v2.2 foi calibrada para:
- **LGPD by design** (datacenter BR, isolamento físico multi-tenant, redact em logs)
- **Velocidade de desenvolvimento** (Node/Nest/Prisma stack TypeScript end-to-end)
- **Custo operacional baixo** (self-host Proxmox para tudo que não é deliverability/edge)
- **Velocidade de iteração** (Nx affected, code-splitting, hot reload Vite Rolldown)
- **Solo founder mode** (toolchain conhecido pelo PO, sem learning curve)
Para alterar qualquer decisão fora desta tabela: RFC em `docs/adr/NNN-titulo.md`.
---
## Integrations (Step 29)
### Decisões fechadas
| Integração | Provider escolhido | Por quê | Notas |
|---|---|---|---|
| **Pagamento** | **Iugu** | BR-focused, assinaturas recorrentes nativas, boleto/PIX/cartão suportados, ticket B2B BR padrão | Webhook handler via NestJS controller + BullMQ para retries. Receita reconhecida no momento do `paid` event. |
| **WhatsApp Business** | **Oficial Meta (WhatsApp Cloud API)** | Sem intermediário, templates aprovados direto pela Meta, mais seguro a longo prazo | Setup mais burocrático (verificação Business + templates approval). Custo por mensagem. Templates centralizados em `libs/shared/whatsapp-templates`. |
| **Analytics de produto** | **PostHog self-host** (Proxmox) | Self-host alinha STACK.md (BR + LGPD). Cobre product analytics, session replay, feature flags. | Roda em VM/LXC Proxmox separada. Eventos via SDK no frontend. Dashboards customizados por cockpit. |
| **Email transacional** | **Resend** (já em STACK.md §15) | Já decidido | React Email para templates · BullMQ jobs · DPA assinado |
| **Identidade (IdP)** | **master-login** (próprio, JCS) — ADR 0005 | Já decidido | OAuth2/OIDC, argon2id |
| **Secrets** | **HashiCorp Vault** self-host | Já decidido | KV v2, Vault Agent no container |
| **Object storage** | **MinIO** self-host | Já decidido | S3-compat, presigned POST policy, ClamAV worker |
| **CDN edge** | **Cloudflare** | Já decidido em STACK.md §18 | DNS, edge cache, DDoS, certificados |
| **Logging** | **Grafana Cloud LGTM + Sentry** | Já decidido | OTel + Pino + Sentry |
| **Geolocation (GPS check-in)** | Browser `navigator.geolocation` nativo | Sem provider externo | Web API padrão |
### ⚠️ Decisão aberta: IA generativa
Julian quer estudar todas as opções antes de cravar. **Recomendação canônica: abstração multi-provider desde dia 1.**
**Arquitetura proposta:**
- **Camada de abstração:** **Vercel AI SDK** (`ai` package) ou **LiteLLM proxy** (Node SDK)
- Cliente NestJS chama interface única; troca de provider é configuração
- Providers candidatos a A/B (com critérios de avaliação):
| Provider | Pros | Cons | Score qualitativo |
|---|---|---|---|
| **Anthropic (Claude)** | Excelente em raciocínio analítico + pt-BR · JSON estruturado nativo · explicabilidade forte (alinha SAR) | Custo médio · ecossistema menor que OpenAI | ⭐⭐⭐⭐⭐ alinhamento com diferencial |
| **OpenAI (GPT)** | Ecossistema maior · mais SDKs/tools · function calling maduro | Drift de qualidade entre versões · custo competitivo | ⭐⭐⭐⭐ |
| **Local/Ollama** | Sem custo recorrente · máximo controle/privacidade | Qualidade inferior · custo de infra Proxmox · velocidade variável | ⭐⭐ para MVP, ⭐⭐⭐⭐ se LGPD for ultra-rigoroso |
| **Multi-provider (LiteLLM)** | Hedge total · pode usar local para tarefas simples + cloud para complexas | Complexidade de manutenção | ⭐⭐⭐⭐⭐ estratégico |
**Princípio:** estrutura o código para ser **provider-agnostic**. PR de IA não tem `import OpenAI from 'openai'` — usa interface JCS.
**Marco de decisão:** quando 1º cliente fechar e Julian tiver tempo de benchmark dedicado. Até lá, padrão temporário = **Anthropic Claude Sonnet/Opus** via SDK direto, mas isolado atrás de interface.
### Categorias de integração futura (mapa, sem decisão MVP)
| Categoria | Quando decidir | Candidatos |
|---|---|---|
| **CRM próprio JCS** (vender SAR com SAR — dogfood) | Y1 | O próprio SAR adaptado |
| **Assinatura digital de contratos** | Y1 | DocuSign · ClickSign (BR) · D4Sign (BR) |
| **NFe / NFSe (cobrança JCS para clientes)** | Y1 quando volume justifica | NFe.io · Bling · Iugu pode embutir |
| **CDP / nurturing de leads** | Y1+ | PostHog cobre boa parte; eventualmente HubSpot/RD Station |
| **Helpdesk / suporte ao cliente** | Pós-MVP | Crisp · Zendesk · self-host Cal.com + email |
| **Status page pública** | Pós-MVP | Statuspage.io · Instatus · self-host Cachet |
### Account ownership (quem cria/gerencia)
| Integração | Conta de quem | Notas |
|---|---|---|
| Iugu, Resend, Anthropic/OpenAI, Cloudflare, Meta WhatsApp | **JCS Sistemas** | Único proprietário; cobrança vai pra JCS |
| MinIO, master-login, Vault, GrowthBook, PostHog | **JCS Sistemas self-host** | Roda em VMs Proxmox JCS |
| Google Business Profile (JCS) | JCS Sistemas | Reivindicar e manter |
---
## Contact Strategy (Step 30)
### Primary contact method
**Form de demo** (`/contato` no site marketing) — alinha com sales-led motion (Step 5).
| Campo | Obrigatório | Por quê |
|---|---|---|
| Nome | Sim | Personalização do follow-up |
| Email corporativo | Sim | Canal principal · validador de "B2B real" |
| Telefone (com DDD) | Sim | Permite ligação direta · WhatsApp follow-up |
| Empresa | Sim | Qualificação inicial |
| Cargo / função | Sim | Identifica se é dono/diretor (decisor) ou supervisor (influenciador) |
| Quantos representantes externos | Sim | Qualificação principal (sweet spot 5-50) |
| ERP atual | Sim | Identifica origem do lead (Grupo 2 da concorrência) |
| Cidade/UF | Sim | Roteamento regional eventual |
| Como nos conheceu | Opcional | Atribuição de canal (indicação? Google? feira? parceiro?) |
| Mensagem | Opcional | Captura intent custom |
### Channels (canais de contato disponíveis)
| Canal | Quando aparece | Quem responde |
|---|---|---|
| **Form de demo** (CTA principal) | Em toda página + header sticky | SDR JCS (Julian no início) |
| **Email** (`contato@sarjcs.com.br` ou similar) | Footer | SDR |
| **Telefone JCS** | Footer + página `/contato` | SDR (horário comercial BR) |
| **WhatsApp JCS Business** | Footer + página `/contato` (clica e abre app) | SDR |
| **Página `/parceiros`** | Captura indicações de canal | Account manager (Julian no início) |
| **Chat ao vivo** | ❌ NÃO no MVP | Volta como decisão pós-MVP |
### Form requirements técnicos
- Anti-spam: **hCaptcha** ou **Cloudflare Turnstile** (não Google reCAPTCHA — LGPD)
- Validação client-side: Zod schema compartilhado com o backend
- Submit → cria lead no CRM JCS · dispara notificação Slack/email para SDR · responde "Recebemos seu contato — Julian responde em até 4h úteis"
- Rate limit: 5 submissions/email/h · 10/IP/h via `@nestjs/throttler`
- LGPD: checkbox de consentimento explícito + link para política de privacidade
- Idempotency: `Idempotency-Key` header (Zod + nestjs-zod já cobrem)
### AI opportunity em contato
**Pós-MVP:** chatbot pré-qualificador do lead na própria página de contato. Usa o stack de IA generativa (a definir) para responder dúvidas básicas e qualificar antes do SDR atender. **Não no MVP** — solo founder não vai operar isso bem.
### UX implications
- Form aparece em modal/drawer ao clicar "Agende demo" (não navegação a `/contato`)
- Após submit, mensagem clara + próximos passos ("Julian envia uma proposta em até 24h úteis")
- Email automático imediato confirmando recebimento (Resend + React Email)
- Lead score automático no CRM (tamanho da equipe + cargo)
---
## Multilingual & SEO (Step 31)
### Multilingual (já decidido em Step 16)
- **pt-BR only no MVP** · arquitetura i18n-ready (todas strings em `pt-BR.json`)
- **en** preparado no código, sem tradução
- **es (LatAm)** fora de escopo (requer adaptação fiscal por país)
### URL structure técnica
- Domínio: **`sarjcs.com.br`** (proposta — verificar disponibilidade)
- pt-BR (default): `<dominio>/<slug>`
- en futuro: `<dominio>/en/<slug>`
- App produto: `app.sarjcs.com.br/` ou `<dominio>/app/` (decidir em PRD)
- Slug: lowercase, hífen, ASCII, sem stopwords desnecessárias
### Translation workflow (futuro)
- Strings vivem em `libs/shared/i18n/locales/pt-BR.json` (single source)
- Tradução `en.json` quando ativar: copy mantido fiel ao tom "consultor sênior" + variantes culturais aplicáveis
- Sincronização via diff de chaves: CI quebra build se chave faltar em algum locale ativo
### Technical SEO requirements (espelha Step 17)
| Item | Implementação |
|---|---|
| **Meta tags dinâmicas** | Vite + React Helmet por rota |
| **Sitemap.xml** | Gerado por script no build · submit Google Search Console |
| **robots.txt** | Permitir crawling em marketing; **noindex em `/app/`** (produto privado) |
| **Structured data (JSON-LD)** | `Organization` (JCS) · `SoftwareApplication` (SAR home) · `Product`+`Offer` (preços) · `Article` (blog/cases) · `FAQPage` · `BreadcrumbList` |
| **Open Graph + Twitter Cards** | Por rota, com imagem dedicada (screenshot do produto) |
| **hreflang tags** | Não no MVP (single language); preparado quando i18n ativar |
| **Canonical URLs** | Em toda página, mesmo o domínio principal |
| **404 customizada** | Line art mono JCS Blue + "Voltar pra home" |
| **Core Web Vitals** | Lighthouse > 90 · LCP < 2.5s · CLS < 0.1 · INP < 200ms |
| **Schema.org test** | Validação no Google Rich Results Test antes de cada deploy |
### Performance budgets (espelha Step 24)
| Métrica | Target | Bloqueio |
|---|---|---|
| Lighthouse Performance (mobile) | ≥ 90 | CI quebra se < 85 |
| FCP | < 1.5s | — |
| LCP | < 2.5s | CI alerta se > 3s |
| CLS | < 0.1 | — |
| INP | < 200ms | — |
| Bundle inicial por entry | < 200KB gzipped | CI alerta se > 250KB |
| TTI Rafael 3G (PWA) | < 3s | Validação manual em dispositivo |
---
## Maintenance Ownership
### MVP (solo founder mode)
| Categoria | Owner | Frequência |
|---|---|---|
| Stack updates (npm deps, ADRs, RFC) | **Julian** | Trimestral (alinhar com STACK.md cadence) |
| Deploys (backend + frontend) | **Julian** via Ansible/SSH | A cada PR mergeado |
| Infraestrutura Proxmox (VMs, snapshots, backup) | **Julian** | Manutenção mensal, backup diário automatizado |
| Vault rotation, master-login chaves | **Julian** | Trimestral / on-incident |
| Monitoramento Grafana + Sentry | **Julian** | On-call 24/7 (PME pequena) |
| Integrações (Iugu, Resend, Meta WhatsApp, Cloudflare) | **Julian** | On-issue |
| LGPD ANPD / DPO | **Julian** (com possível advogado externo) | On-request |
| Atualização de templates WhatsApp Meta | **Julian** + revisor de tom | On-mudança |
### Pós-MVP (Y1+)
| Categoria | Owner futuro |
|---|---|
| Infraestrutura | DevOps/SRE dedicado |
| Backend dev | Time JCS (1-2 devs) |
| Frontend dev | Time JCS (1-2 devs) |
| QA | QA dedicado ou compartilhado com dev |
| LGPD / Compliance | DPO formal (Julian acumula, depois separa) |
---
## Risk register
| Risco | Probabilidade | Impacto | Mitigação |
|---|---|---|---|
| Meta muda regras WhatsApp Business | Média | Alto | Arquitetura plug-in · alternativa Telegram/SMS no roadmap (Step 9, 10) |
| Anthropic/OpenAI muda pricing ou política | Média | Alto | Abstração multi-provider (LiteLLM/AI SDK) · capacidade de trocar provider · sempre considerar local fallback |
| Iugu instabilidade | Baixa | Alto | Multi-provider de pagamento (preparar arquitetura para alternar Pagar.me como backup) |
| Cloudflare CDN issue | Baixa | Médio | Migrar para AWS CloudFront / Bunny CDN possível em <1 dia |
| Proxmox failure | Baixa | Crítico | Backup snapshot diário · Plano B: re-provisionamento em outra infra (custo de RTO ~4-8h) |
| Solo founder ausência prolongada | Média | Crítico | Documentação extensiva em CODING-RULES.md · todos os secrets em Vault recuperável · contratar 1 dev assim que possível |
---
## Final synthesis
### Visão executiva
O SAR roda na stack canônica **JCS v2.2** sem desvios. Decisões de plataforma específicas do produto vivem em **Step 10a (Platform Strategy)** e neste documento. **STACK.md é a fonte da verdade** para qualquer questão técnica não específica do SAR.
### Tabela síntese (entrar no Brief executivo)
```
─────────────────────────────────────────────────────────
SAR — Platform Requirements Summary
─────────────────────────────────────────────────────────
Stack: Node 24 · NestJS 11 · Prisma 7 · PG 18 · React 19 + Vite 8
+ AntD 6.4 · Zod 4 · BullMQ 5.77 · Socket.IO 4 · TanStack
Query/Router · Zustand
Multi-tenant: BD-por-workspace (cluster PG dedicado/cliente) — ADR 0006
Auth: master-login (IdP próprio) — ADR 0005
Infra: Proxmox on-prem BR — ADR 0004
Distribução: PWA mobile-first iOS · Desktop-first · iPad first-class
Integrations:
Pagamento: Iugu (boleto/PIX/cartão + assinaturas recorrentes)
WhatsApp: Oficial Meta (Cloud API)
IA: Decisão ABERTA — abstração multi-provider (LiteLLM/AI SDK)
· default temporário: Anthropic Claude
Analytics: PostHog self-host (Proxmox)
Email: Resend (transacional)
Storage: MinIO self-host
Secrets: Vault self-host
CDN: Cloudflare
Observ.: Grafana LGTM + Sentry + OpenTelemetry
Contact: Form de demo (CTA único) · sem chat ao vivo no MVP
Anti-spam: hCaptcha/Turnstile · Throttler 5/h por email
Lang: pt-BR only no MVP · i18n-ready · sem hreflang até ativar en
SEO técnico: Lighthouse >90 mobile · JSON-LD · sitemap · noindex /app/
Maintenance: Julian (solo founder mode) até 1º cliente; depois +1-2 devs
─────────────────────────────────────────────────────────
```
### Próximos passos
- **Phase 3 (PRD)**: detalhar requirements por feature consumindo este doc
- **Phase 4 (UX Specs)**: cada cockpit referencia constraints de plataforma
- **Phase 5 (Agentic Dev)**: scaffolding Nx + setup CI/CD via STACK.md
**Block D COMPLETO. Próximo: Block E — Wrap-up (Steps 33-36).**