Pular para o conteúdo

Builder OS

Builder · OS
apx · Decisões de stack
FIM DO MÓDULO
~10 MIN DE LEITURA

Apêndice: Decisões de stack

apêndice do módulo 3
AO FIM, VOCÊ VAI TER
  • Mapa das alternativas de stack que o Módulo 3 não explorou
  • Identificação de qual sub-seção se aplica ao seu projeto

O Módulo 3 assume a stack default do starter: Next.js App Router + Postgres + Drizzle + Zod + Route Handlers. Funcionou. Mas seu projeto pode ter escolhido outra coisa: Supabase em vez de Postgres puro, Server Actions em vez de Route Handlers, Prisma em vez de Drizzle. Este apêndice é o mapa.

Cada sub-seção tem 3 partes: o que muda no Módulo 3 (quais passos precisam de adaptação), o que não muda (princípios universais) e decisão registrada (se você escolheu essa via, comita uma linha no docs/decisions.md do projeto).

O apêndice não está aqui pra te converter. Se você já tomou a decisão, beleza: aqui está o que muda.

Drizzle vs Prisma

O que muda:

  • L02 (schema): sintaxe do schema é diferente. Drizzle usa funções TypeScript (pgTable, varchar, bigint); Prisma usa DSL própria no schema.prisma. Migration é gerada de forma diferente (drizzle-kit generate vs prisma migrate dev).
  • L04 (API route): sintaxe do insert. Drizzle: db.insert(recibos).values(data).returning(). Prisma: db.recibo.create({ data }). Ambos retornam o objeto inserido.
  • Tratamento de erros do banco (L04): os códigos de erro do Prisma vêm como P2002 (unique constraint) e P2003 (FK); o Drizzle expõe os códigos Postgres crus (23505, 23503). A lógica de tradução muda, mas o resultado (409, 400) continua o mesmo.

O que não muda:

  • Validação na borda com Zod (L03) — Zod não depende de ORM
  • Decisão de tipos brasileiros (bigint em centavos, varchar(11) em CPF) — vale igual
  • Pattern de 1 commit por tarefa, 1 PR por feature — vale igual

Decisão registrada: se você usa Prisma, comita 1 linha em docs/decisions.md: "ORM: Prisma. Razão: equipe já conhecia, ou tooling de migração mais maduro, ou outro motivo seu."

Server Actions vs Route Handlers

O que muda:

  • L04 inteira. Server Actions movem o "POST /api/recibos" pra um Server Component (ou async function dentro do mesmo arquivo do form). O contrato HTTP some: você chama uma função TypeScript que roda no servidor.
  • L05 (form): o form não tem fetch('/api/recibos'); tem action={createRecibo} onde createRecibo é a Server Action.
  • Testes E2E: ficam um pouco diferentes. Em vez de testar a API com supertest ou similar, você testa a Server Action chamando ela como função (Vitest/Jest com mock de DB).

O que não muda:

  • Validação na borda com Zod (L03) — a Server Action ainda recebe um payload externo (FormData ou JSON) e precisa validar
  • Tratamento de erros do banco (unique constraint, FK): a Server Action joga exception e o componente cliente captura via useFormState ou try/catch
  • Evento rastreado (L06): a chamada analytics.track() continua sendo feita no ponto de sucesso, só que dentro da Server Action

Decisão registrada: se você usa Server Actions, comita 1 linha: "API: Server Actions. Razão: stack mais coesa com Server Components, menos boilerplate, ou outro motivo seu."

Aviso prático: Server Actions em Next.js ainda são relativamente novas (estáveis desde o Next 14). Algumas libs (principalmente de teste e de fila) ainda têm tooling mais maduro para Route Handlers. Se você está construindo solo, qualquer um serve. Se está em equipe maior, Route Handlers tende a dar menos surpresa.

Supabase vs Postgres puro

O que muda:

  • L02 (schema): você pode escrever schema no Drizzle e usar Supabase como host de Postgres (caminho recomendado se você quer escolha técnica). Ou pode usar o Studio do Supabase e escrever schema visualmente — funciona, mas perde versionamento no repo. Recomendação: schema no código.
  • Conexão: DATABASE_URL vem do dashboard Supabase. Suporta connection pooling (porta 6543) e direct connection (porta 5432). Use a 6543 em runtime; a 5432 só pra migrations.
  • Row Level Security (RLS): o Supabase recomenda RLS por padrão. Pra fatia 1 ainda sem auth, desabilite RLS na tabela ou crie políticas permissivas. Senão, o insert da L04 vai falhar com "permission denied for table." A L04 não cobre RLS; configura na sua tabela conforme a necessidade.

O que não muda:

  • Schema, validação, API route — tudo igual a Postgres puro
  • Sintaxe SQL, tipos, índices — igual

Decisão registrada: "DB: Supabase Postgres. Razão: setup zero, dashboard útil, auth integrado pra futuro."

Aviso prático: o Supabase tem auth, storage e realtime embutidos. Não use eles ainda na fatia 1. Cada um adiciona complexidade que não dá pra reverter sem esforço. Use só o Postgres por enquanto e adicione os outros quando o produto exigir.

PostHog vs Plausible (analytics)

Você não escolheu nada na L06 — só criou analytics.track() como no-op. A escolha real vem no Módulo 4. Mas se quiser antecipar a decisão, aqui está o mapa.

PostHog:

  • Event-based (você manda eventos com propriedades arbitrárias)
  • Free tier: 1M eventos/mês — suficiente pra fatia 1 e meses seguintes
  • Suporta funil, retenção, A/B test, session replay
  • Setup: precisa de SDK no client + cookie consent (LGPD)
  • Pra quem é: builder que quer analytics de produto sério

Plausible:

  • Page view based (eventos custom são limitados)
  • Free tier: nenhum free real — $9/mês minimo
  • Cookieless por design — não precisa banner de consent
  • Pra quem é: builder cívico/público que quer LGPD-friendly out of the box

Decisão registrada quando escolher: "Analytics: PostHog. Razão: free tier generoso + eventos custom suficientes pro funil." Ou: "Analytics: Plausible. Razão: produto público, sem cookie banner, custo previsível."

TypeScript strict mode

O Starter já vem com strict: true no tsconfig.json. Essa decisão já está tomada: não desabilite. Toda lição do Módulo 3 assume strict ligado. Se você desabilitou, vai começar a aceitar bugs que o strict pegava (null check faltando, type assertion errada).

Decisão registrada: não precisa registrar — strict é default do Starter. Se você desabilitou, registra a razão.

Onde isso vive depois do M3

docs/decisions.md (ou decisions/, pasta com 1 arquivo por decisão maior — formato ADR) é convenção do M5. O Módulo 5 vai te ensinar a manter decisões versionadas como o CEAP faz (você viu CEAP/DECISIONS.md em várias lições do M3).

Por enquanto, uma frase commitada em docs/decisions.md já resolve: "DB: X. ORM: Y. API: Z." Se algum dia outro builder olhar o repo, vai entender as escolhas em 30 segundos.