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 Notification por 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.py e tasks.py nã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_expired do banco. Por isso, todo ponto que marca um device como expirado precisa também chamar unsubscribe(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

Referências