R-010 — Model Notification é reservado a envios pontuais
TLDR: Hábitos e lembretes enviam direto ao FCM em lote e nunca criam linhas
Notification; o model é exclusivo de envios pontuais (áudio, admin, envio particular).
Given / When / Then
Dado um envio pontual — mensagem criada no admin, lançamento de áudio (send_to_all=True, agendado) ou envio direcionado a um push_device específico
Quando o envio é disparado
Então uma linha Notification é criada e o pipeline post_save → task Celery → messaging.send a despacha
Dado uma execução de hábito ou uma onda de lembrete
Quando o envio é disparado
Então nenhuma linha Notification é criada — as mensagens vão direto ao FCM em lote via apps/notifications/services/push_batch.send_batch (messaging.send_each)
Tabela de decisão
| Fluxo | Cria Notification? |
Caminho de envio |
|---|---|---|
| Mensagem criada no admin | Sim | post_save → send_notification.delay → messaging.send |
Lançamento de áudio (send_to_all) |
Sim | idem, agendado por scheduled_at |
Envio a um push_device específico |
Sim | idem |
| Notificação inicial de hábito | Não | send_batch → messaging.send_each |
| Ondas de lembrete de hábito | Não | send_batch → messaging.send_each |
Restrições
- O motivo é volume: para N usuários com M devices, criar
Notificationpor device gera N×M linhas no banco, N×M tasks Celery e N×M chamadas HTTP ao FCM por janela. Hábitos e lembretes são justamente os fluxos de maior volume apps/notifications/models.py,signals.py,services/notification.pyetasks.pynão devem ser alterados para acomodar hábitos — eles servem o fluxo pontual- O envio para tópico (usado pelo canal “All”) é feito pelo Firebase e ignora a flag
is_expireddo banco. Por isso, todo ponto que marca um device como expirado precisa também chamarunsubscribe(device, device.topics)— sem isso, tokens antigos ainda entregáveis seguem no tópico e o aparelho recebe cópias duplicadas
Teste vinculado
tests/habits/ — testes dos services de hábito/lembrete com messaging.send_each mockado, verificando que nenhuma Notification é criada
tests/notifications/test_expired_fcm_token.py, tests/notifications/test_topic_subscription.py — expiração de device dispara unsubscribe_from_topic