100 lines
4.2 KiB
Markdown
100 lines
4.2 KiB
Markdown
# FASE 1 — Domínio compartilhado e camada de dados
|
|
|
|
## Objetivo
|
|
|
|
Modelo de domínio em `shared/`, schema completo no Postgres, infraestrutura de confiabilidade (outbox, fila, idempotência) funcionando, e autenticação. Ainda sem endpoints de negócio.
|
|
|
|
## Pré-requisitos
|
|
|
|
FASE 0 concluída. Ler `docs/02-MODELO-DE-DADOS.md` inteiro e `docs/01-ARQUITETURA.md` §4 e §5.
|
|
|
|
## Tarefas
|
|
|
|
### 1. Domínio em `shared/`
|
|
|
|
`state/VisitState.kt` e `VisitStateMachine.kt` conforme `01-ARQUITETURA.md` §4 e o diagrama de `03-FLUXOS-E-CONTRATOS.md` §1. A máquina de estados é a peça mais importante deste módulo — **teste toda transição válida e inválida**.
|
|
|
|
`model/` — enums e value objects: `VisitKind`, `DeliveryRule`, `MemberRole`, `MediaKind`, `DegradedMode`, `FeatureKey`, `GeoPoint`.
|
|
|
|
`dto/` — DTOs `@Serializable` de todos os contratos de `03-FLUXOS-E-CONTRATOS.md` §6.
|
|
|
|
`validation/` — validação de nome de visitante, telefone E.164, formato de unidade, tamanho de mídia. Usada no cliente e no servidor.
|
|
|
|
### 2. Migrations Flyway
|
|
|
|
As dez migrations de `02-MODELO-DE-DADOS.md` §11, exatamente na ordem indicada. Pontos que não podem ser esquecidos:
|
|
|
|
- `tenant_id uuid NOT NULL` em **toda** tabela de negócio
|
|
- `version integer NOT NULL DEFAULT 0` onde há escrita concorrente
|
|
- Nenhum `ON DELETE CASCADE` em dado auditável
|
|
- Índice GIST em `gates.location`
|
|
- Índice parcial em `visits` para estados ativos
|
|
- `V8` revoga `UPDATE`/`DELETE` em `audit_log` para o usuário da aplicação
|
|
- `V9` cria as policies de RLS e as deixa **desativadas**
|
|
|
|
### 3. jOOQ
|
|
|
|
Geração de código a partir do schema migrado (Testcontainers no build, não banco local). Repositórios para os agregados principais. **Todo repositório recebe `tenantId` como parâmetro obrigatório** — sem default, sem opcional. É o que torna a ativação futura do multi-tenant um interruptor.
|
|
|
|
### 4. Outbox transacional
|
|
|
|
```kotlin
|
|
interface OutboxPublisher {
|
|
fun enfileirar(evento: DomainEvent) // participa da transação corrente
|
|
}
|
|
```
|
|
|
|
Publisher que lê `outbox_events` pendentes com `FOR UPDATE SKIP LOCKED`, despacha, marca `published_at`, e em falha aplica backoff exponencial (`next_attempt_at`) e grava `last_error`. Após 10 tentativas, alerta.
|
|
|
|
**Teste obrigatório:** transação que falha após `enfileirar()` não pode deixar evento na tabela. É exatamente a garantia que o padrão existe para dar.
|
|
|
|
### 5. Fila de jobs e ShedLock
|
|
|
|
```kotlin
|
|
interface JobScheduler {
|
|
fun agendar(tipo: JobType, payload: JsonObject, quando: Instant): Long
|
|
fun cancelar(jobId: Long)
|
|
}
|
|
```
|
|
|
|
Poller `@Scheduled(fixedDelay = 1000)` + `@SchedulerLock`, consumindo com `FOR UPDATE SKIP LOCKED`. Handlers registrados por `JobType`.
|
|
|
|
**Teste obrigatório com duas instâncias simultâneas:** cada job executa exatamente uma vez.
|
|
|
|
### 6. Idempotência
|
|
|
|
Filtro/interceptor que lê `Idempotency-Key`, consulta `idempotency_keys`, e:
|
|
|
|
- chave nova → executa e grava a resposta
|
|
- chave repetida com mesmo `request_hash` → devolve a resposta original
|
|
- chave repetida com corpo diferente → `422 IDEMPOTENCY_KEY_REUSE`
|
|
|
|
### 7. Segurança base
|
|
|
|
Spring Security com três cadeias de filtro conforme `06-LGPD-E-SEGURANCA.md` §6: `/api/v1/visitor/**` (JWT efêmero), `/api/v1/app/**` (OIDC), `/api/v1/admin/**` (OIDC + MFA).
|
|
|
|
Emissão e validação do JWT de visitante: 15 min, escopo de uma visita, atado a `gateId` e IP.
|
|
|
|
### 8. Auditoria
|
|
|
|
Aspecto que grava em `audit_log` toda mutação de entidade auditável, com `before`/`after` em `jsonb`.
|
|
|
|
## Critérios de aceite
|
|
|
|
- [ ] `./gradlew :shared:allTests` verde (JVM + iOS)
|
|
- [ ] Cobertura de 100% das transições da máquina de estados, válidas e inválidas
|
|
- [ ] `flyway migrate` do zero cria o schema completo
|
|
- [ ] jOOQ gera código a partir do schema migrado
|
|
- [ ] Teste: rollback de transação não deixa evento no outbox
|
|
- [ ] Teste: duas instâncias do poller executam cada job exatamente uma vez
|
|
- [ ] Teste: `UPDATE` em `audit_log` falha por permissão do banco
|
|
- [ ] Teste: chave de idempotência repetida devolve a resposta original
|
|
- [ ] Teste: repositório sem `tenantId` não compila
|
|
|
|
## Não faça nesta fase
|
|
|
|
- Endpoints de negócio (FASE 2)
|
|
- Integração com LiveKit, FCM ou APNs
|
|
- Qualquer tela
|
|
- Ativar RLS
|