# PRD — Fase 2: backend no Firebase

**Data:** 07/08/2026 · **Origem:** decisão do dono em 07/08
**Spike de arquitetura:** `docs/spikes/backend-firebase.md`

---

## 1. Problema

O backend atual (NestJS + Prisma + PostgreSQL) atende ao produto, mas **não tem
onde rodar de graça**: as duas últimas tasks de infraestrutura pedem hospedagem
que não hiberne o processo de sincronização e credencial de push. Free tier que
dorme por inatividade faz `PP-6` e `PR-1` pararem **em silêncio** — o prazo fecha
na hora errada e nenhum erro aparece.

Fora isso, três limitações do produto vêm de fora do código: não há recuperação de
senha (sem serviço de e-mail, `AC-4` foi cancelada), não há push (sem FCM) e não há
agendador confiável.

Firebase resolve os três com o mesmo cadastro: Auth com recuperação de senha,
FCM e Cloud Scheduler.

## 2. Objetivo e métrica de sucesso

**Objetivo:** substituir o backend por Firestore + Cloud Functions **sem que
nenhuma regra de domínio mude de comportamento**.

**Métrica:** os **234 casos e2e** e os 40+ casos de autorização passam contra a
stack nova, com os mesmos IDs de caso — comparáveis linha a linha com a execução do
NestJS. Migração cujo veredito é "parece funcionar" não conta.

**Métrica secundária:** custo mensal em R$ 0 no free tier, medido, não estimado.

## 3. Personas afetadas

Nenhuma diretamente — é migração técnica. **É exatamente o que define sucesso:**
se o participante notar, algo quebrou. Indiretamente, todas ganham lembrete de
prazo por push e recuperação de senha.

## 4. Escopo

- Modelo de dados no Firestore, com as regras de acesso em security rules.
- Regra de negócio em Cloud Functions, migrada dos 17 services atuais.
- Autenticação no Firebase Auth, **com** recuperação de senha.
- Jobs de sincronização e lembrete no Cloud Scheduler.
- App apontando para a stack nova.
- Re-execução da suíte de testes contra o Emulator Suite.
- Desligamento do NestJS **somente** depois disso.

## 5. Fora de escopo

- **Mudança de comportamento de qualquer regra.** Se uma regra precisar mudar, é
  outra story, com PRD próprio.
- **iOS com push** — o APNs continua exigindo Apple Developer Program pago.
- Migração de dados de produção: não há produção. A base atual é de
  desenvolvimento.
- Firebase Hosting, Analytics, Crashlytics e Remote Config.

## 6. Regras de domínio decididas

Nenhuma regra nova. As de sempre, com **onde cada uma passa a ser aplicada** —
que é a única coisa que muda:

| Regra | Hoje | No Firebase |
| ----- | ---- | ----------- |
| Prazo é o relógio do servidor | service compara com `new Date()` do servidor | security rule compara `request.time` com o `kickoffAt` do jogo |
| Palpite alheio é segredo até o **fim** do jogo | service filtra por `status`/`homeScore` | flag `jogoTerminado` desnormalizado no palpite + rule; Function vira o flag no apito final |
| Pontuação nunca é calculada no cliente | service de apuração | Function no gatilho de escrita do jogo |
| Empate não tem faixa de saldo (1x1 num 4x4 = 3 pts) | `scoring.service` | mesma função, movida |
| Campeão derivado na leitura, nunca congelado | `ciclo-vida.service` | Function de leitura + agregado recalculado |
| Saída definitiva; criador é admin permanente | `members.service` | rule de participação + Function |
| Tudo não realizado é adiado | `sync.service` | Function agendada |

## 7. Fluxo principal

1. Emulador local de pé; modelo e rules escritos e testados sem custo.
2. Functions recebem a regra dos services, uma story por bloco de risco.
3. App passa a falar com o Firebase por trás dos mesmos repositórios — a camada
   de apresentação não muda.
4. Suíte reexecutada; divergência é defeito de migração, não "comportamento novo".
5. NestJS desligado.

## 8. Requisitos não funcionais

- **Custo R$ 0** no free tier. O ranking **tem** de ser agregado materializado:
  somar pontos na leitura estoura o limite de leituras diárias.
- **Ranking em uma leitura**, independente do número de participantes — o gate de
  `PR-3-11` continua valendo, medindo leituras em vez de consultas.
- **Sem regressão de latência no palpite**, que é o momento crítico: escrita direta
  no Firestore com rule, sem Function no caminho.
- Testes rodam em emulador, nunca contra projeto real.

## 9. Riscos e dependências

- 🔴 **Cloud Functions exige plano Blaze** — cartão cadastrado, ainda que a fatura
  fique em R$ 0. Contraria a restrição de 03/08 e **depende de decisão do dono**.
  Enquanto não houver, a migração fica pronta e verificada nos emuladores, sem
  publicar.
- 🔴 **O sigilo do palpite muda de mecanismo**, de filtro em consulta para flag
  desnormalizada. O risco deixa de ser "a consulta vaza" e passa a ser "a Function
  não virou o flag". Falha em uma direção esconde palpite a mais (aceitável); na
  outra, expõe antes do fim do jogo (grave).
- **Limite de 500 operações por transação** no Firestore: apurar bolão grande
  precisa de lote, não de transação única.
- **Sem `join`**: o extrato passa a compor duas leituras em memória.
- **Cold start** na hora do apito, quando todos palpitam junto.
- A trava de sincronização concorrente (07/08) precisa virar lock em documento —
  em processo não serve, cada invocação é um processo novo.

## 10. Perguntas abertas

| Pergunta | Prazo | Dono |
| -------- | ----- | ---- |
| Habilitar Blaze (cartão, fatura esperada R$ 0)? | antes do deploy | Igor |
| `AC-4` (recuperação de senha) volta do cancelamento agora que o Auth a dá de graça? | antes de `MG-4` | Igor |

## 11. Stories derivadas

Os Given/When/Then vivem no cartão de cada story (`docs/stories/`), não aqui. A
ordem segue o **risco**, não a facilidade.

- [ ] `MG-1` Projeto Firebase e emuladores rodando local, sem custo
- [ ] `MG-2` Modelo de dados no Firestore e regras de participação
- [ ] `MG-3` Sigilo do palpite e prazo aplicados nas security rules
- [ ] `MG-4` Autenticação no Firebase Auth, com recuperação de senha
- [ ] `MG-5` Apuração e classificação materializada em Cloud Functions
- [ ] `MG-6` Sincronização e lembretes no Cloud Scheduler
- [ ] `MG-7` App falando com o Firebase pelos mesmos repositórios
- [ ] `MG-8` Paridade verificada: os 234 casos na stack nova
- [ ] `MG-9` Desligar o backend NestJS

---

## Bloqueios

`MG-1` a `MG-8` **não** dependem de decisão financeira: rodam em emulador. Só o
deploy exige Blaze. `MG-9` só começa depois de `MG-8` verde.
