NotificationOutbox

Overview

Este módulo en services/api/src/models/NotificationOutbox.js define el esquema Mongoose que persiste la cola de notificaciones salientes. El modelo guarda el tipo de evento, el destinatario, el canal de entrega, el estado operativo y los metadatos asociados al envío.

También fija reglas de validación, índices de acceso y retención automática. El archivo exporta NotificationOutbox para uso del backend y NOTIFICATION_EVENT_TYPES para reutilizar el catálogo de eventos permitido.

[!NOTE] EVENT_TYPES también vive en notifications/notification.events.js. En este archivo actúa como la fuente de verdad del esquema y debe mantenerse sincronizado con ese módulo cuando se implemente.

How it works

flowchart TD
    E[EVENT_TYPES] --> S[NotificationOutboxSchema]
    S --> V[eventType validation]
    S --> U[recipientUserId ref User]
    S --> C[recipientChannel validation]
    S --> P[payload data]
    S --> T[status tracking]
    S --> A[attempts and lastError]
    S --> D[availableAt and sentAt]
    S --> I[index definitions]
    S --> O[notification_outbox collection]
    N[notification service] --> M[providerMessageId assignment]
    M --> O
    O --> X[MongoDB TTL cleanup]

NotificationOutboxSchema valida cada documento antes de guardarlo en MongoDB. El modelo registra índices para consultar filas por estado, fecha de disponibilidad y usuario. notification.service.js asigna providerMessageId solo tras un envío exitoso, y MongoDB elimina los documentos al vencer la retención.

Key components

Estos son los elementos principales que expone este archivo y su responsabilidad dentro del flujo de notificaciones.

{
    "title": "NotificationOutbox",
    "content": "Modelo Mongoose que persiste la cola de notificaciones en la colecci\u00f3n notification_outbox. Valida eventos, destinatarios, estado de entrega y metadatos operativos.",
    "type": "class",
    "filePath": "services/api/src/models/NotificationOutbox.js",
    "badges": [
        "public"
    ]
}
{
    "title": "NOTIFICATION_EVENT_TYPES",
    "content": "Exportaci\u00f3n p\u00fablica del cat\u00e1logo de eventType admitido por el esquema. Reexpone EVENT_TYPES para reutilizaci\u00f3n en otros m\u00f3dulos.",
    "type": "enum",
    "filePath": "services/api/src/models/NotificationOutbox.js",
    "badges": [
        "public"
    ]
}

Data & fields

El esquema combina campos obligatorios, campos opcionales y campos automáticos generados por Mongoose. La colección fija es notification_outbox, y el modelo usa timestamps: true.

[
    {
        "name": "eventType",
        "type": "string",
        "required": true,
        "description": "Tipo de evento validado contra EVENT_TYPES."
    },
    {
        "name": "recipientUserId",
        "type": "ObjectId | null",
        "description": "Referencia opcional al modelo User. Su valor por defecto es null."
    },
    {
        "name": "recipientChannel",
        "type": "string",
        "required": true,
        "description": "Canal de entrega. Solo admite primary_email o retention_contact."
    },
    {
        "name": "payload",
        "type": "Schema.Types.Mixed",
        "description": "Carga útil flexible del evento. Su valor por defecto es un objeto vacío."
    },
    {
        "name": "status",
        "type": "string",
        "description": "Estado de la fila. Sus valores permitidos son pending, sending, sent, failed y dead_letter."
    },
    {
        "name": "attempts",
        "type": "number",
        "description": "Contador de intentos de envío. Su valor por defecto es 0."
    },
    {
        "name": "lastError",
        "type": "string",
        "description": "Último error registrado durante el envío. Su valor por defecto es una cadena vacía."
    },
    {
        "name": "availableAt",
        "type": "date",
        "description": "Fecha desde la cual el registro queda disponible. Su valor por defecto es Date.now."
    },
    {
        "name": "sentAt",
        "type": "date",
        "description": "Fecha del envío exitoso. Su valor por defecto es null."
    },
    {
        "name": "providerMessageId",
        "type": "string | null",
        "description": "Identificador devuelto por el proveedor tras un envío exitoso. Solo se asigna desde notification.service.js."
    },
    {
        "name": "createdAt",
        "type": "date",
        "description": "Campo automático generado por timestamps. Participa en el índice TTL."
    },
    {
        "name": "updatedAt",
        "type": "date",
        "description": "Campo automático generado por timestamps."
    }
]

eventType

Este campo es obligatorio y controla qué tipo de notificación representa el documento. El esquema valida su valor contra EVENT_TYPES.

  • ACCOUNT_DELETION_REQUESTED
  • ACCOUNT_RESTORATION_LINK_RESENT
  • ACCOUNT_RESTORED
  • ACCOUNT_ANONYMIZED
  • ACCOUNT_PURGE_WARNING
  • ACCOUNT_PURGED
  • PASSWORD_RESET_REQUESTED
  • PASSWORD_CREATED
  • EMAIL_VERIFICATION_REQUESTED
  • WELCOME
  • LISTING_INTEREST_RECEIVED
  • LISTING_COMMENT_RECEIVED
  • LEAD_CREATED_DURING_PROTECTION
  • LEAD_CREATED_DURING_ANONYMIZATION
  • LISTING_PERFORMANCE_DIGEST

recipientChannel

Este campo indica cómo se resuelve el destino del mensaje en el momento del envío. El comentario del código aclara que puede apuntar al email principal del usuario o al retentionContact cifrado.

  • primary_email
  • retention_contact

status

Este campo controla el ciclo de vida operativo de la fila. El valor por defecto es pending, y el esquema lo indexa junto con availableAt.

  • pending
  • sending
  • sent
  • failed
  • dead_letter

timestamps y retención

Mongoose agrega createdAt y updatedAt mediante timestamps: true. El índice TTL usa createdAt como base temporal para la expiración automática. El documento no guarda la dirección cruda del destinatario en este modelo.

Behaviors

El archivo define comportamientos de validación, indexación y persistencia que sostienen el envío de notificaciones.

  • NotificationOutboxSchema.index({ status: 1, availableAt: 1 }) — crea un índice compuesto para consultas por estado y fecha de disponibilidad.
  • NotificationOutboxSchema.index({ recipientUserId: 1, createdAt: -1 }) — acelera las consultas por usuario, ordenadas de más recientes a más antiguas.
  • NotificationOutboxSchema.index({ createdAt: 1 }, { expireAfterSeconds: RETENTION_TTL_SECONDS }) — activa la eliminación automática tras 90 días.
  • mongoose.model('NotificationOutbox', NotificationOutboxSchema) — registra el modelo Mongoose para que otros módulos lo consuman.
  • NOTIFICATION_EVENT_TYPES — expone el mismo catálogo que EVENT_TYPES para compartirlo con otras partes del backend.

Gotchas & edge cases

[!WARNING] providerMessageId no se acepta desde una entrada externa. El comentario del archivo indica que solo se asigna desde notification.service.js después de un envío exitoso.

[!CAUTION] La retención TTL usa una única regla de 90 días basada en createdAt. No distingue por status.

[!NOTE] El propio esquema trata pending y sending como estados transitorios. failed no depende de un proceso automático de diagnóstico posterior.

[!IMPORTANT] El esquema no guarda la dirección de correo en texto plano. Solo persiste el recipientChannel, y la resolución del destino ocurre en el momento del envío.

Este modelo se conecta con otros componentes del backend que participan en la entrega y la auditoría de notificaciones.

  • notifications/notification.events.js — catálogo hermano de eventos que debe permanecer sincronizado con EVENT_TYPES.
  • notification.service.js — servicio que asigna providerMessageId después de un envío exitoso.
  • User — modelo referenciado por recipientUserId.
  • MongoDB TTL monitor — proceso nativo que elimina los documentos cuando vence la retención.
  • Colección notification_outbox — almacenamiento persistente de las filas de notificación.