Um data lake idempotente, autenticado e extensível para os eventos dos grupos — com escolhas de identificador (UUIDv5 vs UUIDv7) e modelagem de membership que equilibram deduplicação, performance de índice e histórico.
A camada de ingest (lado Laravel) do coletor de métricas dos grupos de WhatsApp da He4rt. Um webhook autenticado por HMAC recebe os eventos crus emitidos pelo coletor externo (wpp-tui, repo Node/Baileys separado) e os persiste num data lake. Este PR inclui o tratamento do snapshot de grupo groups.metadata, que popula nome/metadata do grupo e o vínculo participante↔grupo com papel de admin.
Mantém a sessão do WhatsApp e envia todos os eventos crus, sem filtro.
Valida o HMAC (comparação timing-safe) e a presença do X-Event-Id.
Valida o formato do event_id (→ 400 se inválido), checa duplicata e despacha o Job. Responde 202 rápido.
upsert de grupo/participante · firstOrCreate(event_id) · roteia por tipo.
Popula display_name/payload do grupo e sincroniza a membership (membros + admins) em transação.
Duas camadas: short-circuit por event_id no controller + firstOrCreate no Job (seguro sob corrida/retry).
Novos tipos = novo handler + um case no resolveHandler. O Job permanece enxuto.
PKs em UUIDv7 (time-ordered) e a coluna payload (jsonb) com o evento Baileys cru. Só type, FKs e timestamps viram colunas materializadas.
Precisa ser determinístico: o mesmo evento gera o mesmo id, então reenvios são deduplicados. Gerado pelo bot a partir do conteúdo.
Time-ordered: inserts caem no fim do índice B-tree, sem fragmentação. Ideal para tabela de alto volume. (UUIDv4 do HasUuids seria aleatório.)
O tipo uuid nativo ocupa 16 bytes contra ~64 de um hash hex — o índice unique, consultado a cada request, fica ~4× menor. O controller valida o formato e falha cedo, em vez de deixar o banco estourar.
SHA-256 no event_id → 500 (invalid uuid syntax) → bot reenvia em loop
Str::isUuid() → 400 claro, sem tocar o banco
Coerente com "data lake puro": não jogar fora dado que o envelope já entrega.
A reentrada zera o left_at. O sync roda em DB::transaction e tem guarda contra esvaziar o grupo por engano (snapshot sem participants).
Telefone real (sem hash), conteúdo cru e sem TTL — decisão deliberada e temporária para a fase de mapeamento, registrada no docs/adr/0001.
Passivo LGPD a revisitar no endurecimento (hashing, minimização, retenção). Os payloads de exemplo em docs/testes usam apenas dados sintéticos.
HMAC válido → 202+dispatch · assinatura inválida → 401 · sem event-id → 401 · não-uuid → 400 · body inválido → 422 (dataset) · duplicata → 202 · persistência e2e do envelope · groups.metadata e2e + idempotência
grupo + membership com roles · soft-remove + update de role · reentrada zera left_at · payload sem participants não altera membership · upserts, envelope nulo e idempotência do job · vínculo via pivô
Testes verificam comportamento observável (estado no banco), sem reflection/mocks frágeis; decisões críticas têm prova por mutação.