Modelo CommunicationWorkflow
El modelo CommunicationWorkflow representa una regla de automatizacion que define cuando, a quien y que mensaje enviar. Cada workflow pertenece a un evento específico y se ejecuta automáticamente cuando se cumple su condición de trigger.
Campos del Modelo
| Campo | Tipo | Descripción |
|---|---|---|
id | UUID | Identificador único del workflow |
eventId | UUID | Evento al que pertenece el workflow |
name | string | Nombre descriptivo del workflow (ej: "Recordatorio 7 días antes") |
trigger | enum | Evento que dispara la ejecución |
triggerConfig | JSON | Configuración del trigger (ej: { daysOffset: 7 }) |
campaignType | enum | Tipo de campana que se crea al ejecutarse |
channel | string | Canal de comunicación (email, whatsapp, sms, push) |
subject | string | Asunto del mensaje (para email) |
body | string | Cuerpo del mensaje con variables de template |
templateId | UUID (opcional) | Referencia a un template de comunicación predefinido |
recipientFilter | JSON | Filtros para seleccionar destinatarios (estado RSVP, grupo, tags) |
status | enum | Estado actual del workflow |
executionCount | number | Veces que se ha ejecutado exitosamente |
lastExecutedAt | datetime | Fecha y hora de la última ejecución |
createdAt | datetime | Fecha de creación |
updatedAt | datetime | Ultima modificación |
CommunicationWorkflowStatus
| Estado | Descripción |
|---|---|
ACTIVE | El workflow se ejecutara cuando ocurra su trigger |
PAUSED | El workflow está temporalmente deshabilitado; no se ejecutara aunque ocurra el trigger |
COMPLETED | El workflow ha terminado su ciclo (aplica a triggers que solo se ejecutan una vez, como EVENT_DAY) |
CommunicationWorkflowTrigger
| Trigger | Categoría | Descripción |
|---|---|---|
GUEST_ADDED | Evento de dominio | Se dispara al agregar un invitado nuevo al evento |
RSVP_CONFIRMED | Evento de dominio | Se dispara cuando un invitado confirma asistencia |
RSVP_DECLINED | Evento de dominio | Se dispara cuando un invitado declina la invitación |
DAYS_BEFORE_EVENT | Programado | Se ejecuta N días antes de la fecha del evento |
EVENT_DAY | Programado | Se ejecuta el día del evento |
POST_EVENT | Programado | Se ejecuta después del evento (agradecimiento, encuestas, etc.) |
Controller y Servicio
CommunicationWorkflowsController
El controller expone endpoints CRUD protegidos por la cadena estandar de guards (ClerkAuthGuard + UserThrottlerGuard + RoleGuard) y requiere acceso al evento (@RequireEventAccess()).
| Metodo | Ruta | Descripción | Validaciones |
|---|---|---|---|
GET | /communication-workflows?eventId=xxx | Listar workflows del evento | eventId requerido, acceso al evento |
POST | /communication-workflows | Crear workflow | Máximo 10 por evento, trigger valido, channel válido |
PATCH | /communication-workflows/:id | Actualizar workflow | Solo si status es ACTIVE o PAUSED |
DELETE | /communication-workflows/:id | Eliminar workflow | Solo si no tiene ejecuciones en progreso |
PATCH | /communication-workflows/:id/toggle | Alternar entre ACTIVE y PAUSED | No aplica a COMPLETED |
CommunicationWorkflowsService
El servicio implementa la lógica de negocio para la gestión de workflows:
- create() — Valida que no se exceda el limite de 10 workflows por evento, verifica que el trigger y canal sean validos, y crea el registro en base de datos. Si el trigger es programado (DAYS_BEFORE_EVENT, EVENT_DAY, POST_EVENT), registra el cron job correspondiente en Bull.
- update() — Permite modificar nombre, trigger, canal, template, filtros y cuerpo del mensaje. Si cambia el trigger, actualiza o recrea el cron job.
- toggle() — Alterna el status entre ACTIVE y PAUSED. Un workflow pausado no se ejecuta aunque ocurra su trigger. No se puede alternar un workflow COMPLETED.
- delete() — Elimina el workflow y cancela cualquier cron job asociado. Si hay una campana en progreso generada por este workflow, la ejecución actual se completa pero no se generaran nuevas.
Tipos de Triggers
Los triggers se dividen en dos categorías fundamentales según su mecanismo de activación: triggers basados en eventos de dominio y triggers basados en tiempo.
Triggers de Evento de Dominio
Estos triggers reaccionan a acciones que ocurren en el sistema en tiempo real. Cuando un evento de dominio se emite (por ejemplo, un invitado confirma asistencia), el WorkflowEventListenerService evalua todos los workflows activos del evento correspondiente para determinar cuales deben ejecutarse.
GUEST_ADDED: Se ejecuta inmediatamente cuando se agrega un nuevo invitado al evento. Es útil para enviar automáticamente la invitación o un mensaje de bienvenida al invitado recien agregado. El destinatario es siempre el invitado que acaba de ser agregado (no requiere filtro).
RSVP_CONFIRMED: Se ejecuta cuando un invitado confirma su asistencia via el formulario RSVP de la invitación. Permite enviar automáticamente un mensaje de confirmación con detalles del evento (ubicación, horario, dress code). El destinatario es el invitado que confirmo.
RSVP_DECLINED: Se ejecuta cuando un invitado declina la invitación. Puede usarse para enviar un mensaje de agradecimiento o solicitar feedback. El destinatario es el invitado que declino.
Triggers Programados (Time-based)
Estos triggers se ejecutan en momentos específicos relativos a la fecha del evento. Utilizan un scheduler basado en Bull/Redis que ejecuta un cron job a medianoche en la zona horaria del evento. El cron job evalua si algún workflow programado debe dispararse ese día.
DAYS_BEFORE_EVENT: Requiere triggerConfig.daysOffset (número entero positivo). El workflow se ejecuta N días antes de la fecha del evento. Ejemplo: con daysOffset: 7, si el evento es el 15 de abril, el workflow se ejecuta el 8 de abril. Es el trigger más común para recordatorios.
EVENT_DAY: Se ejecuta el mismo día del evento. No requiere configuración adicional. Es útil para enviar un mensaje de "nos vemos hoy" con los ultimos detalles logisticos (direccion, estacionamiento, etc.). Después de ejecutarse, el workflow transiciona automáticamente a COMPLETED.
POST_EVENT: Se ejecuta después del evento. Puede configurarse con triggerConfig.daysOffset para específicar cuantos días después. Es útil para mensajes de agradecimiento, encuestás de satisfacción o compartir fotos de la galería. Después de ejecutarse, transiciona a COMPLETED.
Condiciones Adicionales por Trigger
Cada trigger puede tener condiciones adicionales definidas en el campo recipientFilter del workflow:
| Condicion | Aplica a | Descripción |
|---|---|---|
rsvpStatus | DAYS_BEFORE_EVENT, EVENT_DAY, POST_EVENT | Filtrar por estado RSVP (confirmed, pending, declined) |
guestGroup | Todos | Filtrar por grupo de invitados |
tags | Todos | Filtrar por tags asignados al invitado |
hasPhone | WhatsApp, SMS | Solo invitados con número de teléfono |
hasEmail | Solo invitados con email | |
hasPushToken | Push | Solo invitados con token de push registrado |
Flujo de Ejecución
El flujo de ejecución de un workflow sigue una cadena de servicios con responsabilidades bien definidas. El flujo varia según el tipo de trigger.
Flujo para Triggers de Evento de Dominio
Flujo para Triggers de Evento de Dominio
Flujo para Triggers Programados
Flujo para Triggers Programados
Componentes del Flujo
| Componente | Responsabilidad |
|---|---|
| WorkflowEventListenerService | Escucha eventos de dominio (NestJS EventEmitter). Filtra workflows activos del evento correspondiente que coinciden con el trigger emitido. |
| WorkflowSchedulerService | Cron job que se ejecuta diariamente. Calcula que workflows programados deben dispararse basandose en la fecha del evento y el daysOffset. Respeta la timezone del evento. |
| WorkflowExecutorService | Orquestá la ejecución de un workflow: obtiene destinatarios, invoca el dispatcher, actualiza contadores. Es la pieza central que conecta la detección del trigger con el envio real. |
| RecipientFilterService | Aplica los filtros del recipientFilter del workflow sobre la lista de invitados del evento. Valida que cada destinatario tenga los datos de contacto necesarios para el canal elegido. |
| MessageDispatcherService | Crea la CommunicationCampaign, renderiza el template con los datos de cada invitado, crea los registros CommunicationMessage y encola los jobs en Bull. |
Filtrado de Destinatarios
El RecipientFilterService es responsable de determinar que invitados reciben el mensaje de un workflow. Combina múltiples criterios de filtrado con lógica AND (todos los filtros deben cumplirse).
Criterios de Filtrado
Por estado RSVP:
El filtro rsvpStatus acepta uno o varios estados: CONFIRMED, PENDING, DECLINED, NO_RESPONSE. Esto permite, por ejemplo, enviar un recordatorio solo a invitados que aun no han respondido (PENDING + NO_RESPONSE).
Por grupo de invitados:
El filtro guestGroup acepta uno o varios IDs de grupo. Los grupos son categorías definidas por el organizador (familia del novio, amigos de la novia, compañeros de trabajo, etc.).
Por tags:
El filtro tags acepta uno o varios tags. Los tags son etiquetas libres asignadas a invitados para segmentacion flexible.
Por datos de contacto: El servicio valida automáticamente que el destinatario tenga el dato de contacto necesario para el canal del workflow:
- Email: requiere campo
emailno vacio - WhatsApp/SMS: requiere campo
phoneno vacio - Push: requiere al menos un
pushTokenregistrado en el dispositivo
Los invitados que no tienen el dato de contacto requerido se excluyen silenciosamente (no generan error, simplemente no reciben el mensaje).
Ejemplo de recipientFilter
{
"rsvpStatus": ["PENDING", "NO_RESPONSE"],
"guestGroup": ["family-bride"],
"tags": ["vip"]
}
Este filtro selecciona invitados que: (1) no han respondido al RSVP, (2) pertenecen al grupo "familia de la novia", y (3) tienen el tag "vip". Los tres criterios deben cumplirse simultaneamente.
Integración con Sistema de Notificaciones
Los workflows reutilizan la arquitectura de 3 capas de notificaciones existente en Nvito. No implementan su propio sistema de envio, sino que generan campanas y mensajes que fluyen a traves de la infraestructura ya probada.
Arquitectura de 3 Capas
Integracion Workflows con Sistema de Notificaciones (3 Capas)
Capa 1 — NotificationChannelsModule (canales puros): Servicios de envio sin lógica de negocio. Cada servicio sabe como enviar un mensaje por su canal específico. No tienen dependencia a Bull ni a la base de datos.
EmailService— Envio via SMTP (nodemailer). Soporta HTML templates con variables.WhatsAppService— Envio via Twilio API. Soporta templates aprobados de WhatsApp Business.SmsService— Envio via Twilio API. Mensajes de texto plano.PushSenderService— Envio via Expo Push API. Notificaciones push a iOS y Android.
Capa 2 — QueueModule (colas Bull/Redis): Maneja el encolamiento y procesamiento asincrono de jobs. Cada canal tiene su propia cola con concurrencia y reintentos configurables.
EmailProcessor— Procesa jobs de email. Concurrencia configurable.WhatsAppProcessor— Procesa jobs de WhatsApp. Concurrencia limitada por rate limits de Twilio.PushProcessor— Procesa jobs de push. Batch de hasta 100 notificaciones por request a Expo.
Capa 3 — NotificationsModule (API de alto nivel): Expone la API pública que los workflows (y otros módulos) consumen. Maneja la lógica de creación de campanas, mensajes, templates y metricas.
Vinculacion Workflow-Campana
Cada vez que un workflow se ejecuta, crea una nueva CommunicationCampaign con el campo workflowId vinculado. Esto permite:
- Rastrear cuantas campanas ha generado un workflow
- Ver el historial de envios por workflow en el panel admin
- Distinguir campanas manuales (sin workflowId) de campanas automatizadas
Retry Policy y Ciclo de Vida de Mensajes
Ciclo de Vida del CommunicationMessage
Cada mensaje individual creado por un workflow sigue un ciclo de vida con estados bien definidos:
Ciclo de Vida del CommunicationMessage
pending→queuedJob encolado en Bullqueued→sentProveedor acepta el mensajesent→deliveredWebhook confirma entregadelivered→openedWebhook confirma aperturaqueued→failedError despues de 3 reintentossent→bouncedWebhook reporta rebotesent→failedTimeout sin confirmacionEstados del Mensaje
| Estado | Descripción | Siguiente paso |
|---|---|---|
PENDING | Mensaje creado, aun no encolado | Se encola en Bull |
QUEUED | Job activo en la cola Bull | Procesador lo toma y envia |
SENT | El proveedor (SMTP, Twilio, Expo) acepto el mensaje | Espera webhook de confirmación |
DELIVERED | Webhook confirma que el destinatario recibio el mensaje | Para email, puede transicionar a OPENED |
OPENED | El destinatario abrio el email (tracking pixel) | Estado terminal exitoso |
FAILED | Error irrecuperable después de agotar reintentos | Estado terminal de error |
BOUNCED | El email reboto (direccion invalida, buzon lleno, etc.) | Estado terminal, se registra para exclusion |
Politica de Reintentos
Los workflows utilizan la misma politica de reintentos que el sistema de colas general:
| Parametro | Valor | Descripción |
|---|---|---|
| Intentos máximos | 3 | Numero total de intentos (1 original + 2 reintentos) |
| Backoff | Exponencial | El delay se duplica en cada reintento |
| Delay base | 5,000ms | Primer reintento a los 5 segundos |
| Delay máximo | 60,000ms | Tope de 1 minuto entre reintentos |
La secuencia de reintentos es: intento 1 (inmediato) -> fallo -> espera 5s -> intento 2 -> fallo -> espera 10s -> intento 3 -> fallo -> dead letter.
Dead letter: Cuando un mensaje agota sus 3 intentos, su status se actualiza a FAILED y se registra el error completo en el campo errorDetails del CommunicationMessage. No se reintenta más. El contador failedCount de la campana padre se incrementa.
Webhooks de Estado
Para email y WhatsApp, los proveedores notifican cambios de estado via webhooks:
- Email (SMTP): Los servidores SMTP con soporte de DSN (Delivery Status Notification) envian bounces. Los emails con tracking pixel reportan apertura via webhook a
/api/communications/webhook/email. - WhatsApp (Twilio): Twilio envia callbacks de estado (sent, delivered, read, failed) al webhook configurado en
/api/communications/webhook/whatsapp. - Push (Expo): Expo retorna tickets de recibo que se verifican periodicamente. Los tokens invalidos se marcan para exclusion.
Limites y Restricciones
Limites por Evento
| Limite | Valor | Motivo |
|---|---|---|
| Workflows máximos por evento | 10 | Evitar complejidad excesiva y conflictos entre workflows |
| Ejecuciones simultaneas | 1 por workflow | Un workflow no puede ejecutarse si ya tiene una ejecución en progreso |
| Destinatarios por ejecución | Sin limite explicito | Limitado indirectamente por la capacidad de las colas Bull |
Restricciones de Negocio
- Un workflow solo puede pertenecer a un evento. No existe el concepto de workflow global o de organización.
- Los workflows programados (DAYS_BEFORE_EVENT, EVENT_DAY, POST_EVENT) se calculan en la timezone del evento, no UTC.
- Un workflow PAUSED no se ejecuta, pero no pierde su posición en el scheduler. Al reactivarlo, se ejecutara en la proxima ventana valida.
- Los workflows COMPLETED no pueden reactivarse. Para repetir un workflow completado, el usuario debe crear uno nuevo.
- Los triggers GUEST_ADDED, RSVP_CONFIRMED y RSVP_DECLINED pueden ejecutarse múltiples veces (una por cada invitado que dispare el evento). Los triggers EVENT_DAY y POST_EVENT se ejecutan una sola vez.
Testing
El sistema de workflows cuenta con tests unitarios que cubren el servicio, controller, listener, executor, filtro y dispatcher.
Areas Cubiertas
CommunicationWorkflowsService:
- CRUD completo (crear, listar, actualizar, eliminar)
- Validación de limite de 10 workflows por evento
- Toggle entre ACTIVE y PAUSED
- Rechazo de toggle en workflows COMPLETED
CommunicationWorkflowsController:
- Autorización: requiere token válido y acceso al evento
- Validación de DTOs con class-validator
- Respuestas correctas para cada endpoint
WorkflowEventListenerService:
- Detección correcta de triggers por tipo de evento de dominio
- Filtrado de workflows ACTIVE (ignora PAUSED y COMPLETED)
- Manejo de múltiples workflows para el mismo trigger
WorkflowExecutorService:
- Ejecución completa del flujo (filtrado -> dispatch -> contadores)
- Incremento de executionCount y lastExecutedAt
- Transición a COMPLETED para triggers de ejecución unica
RecipientFilterService:
- Filtrado por rsvpStatus, guestGroup, tags
- Exclusion de destinatarios sin datos de contacto
- Combinacion AND de múltiples filtros
MessageDispatcherService:
- Creación de CommunicationCampaign con workflowId
- Renderizado de template con variables de invitado
- Encolamiento correcto en el canal correspondiente
Referencias
Modulo de Workflows
- CommunicationWorkflowsController:
src/modules/communications/controllers/communication-workflows.controller.ts - CommunicationWorkflowsService:
src/modules/communications/services/communication-workflows.service.ts - WorkflowEventListenerService:
src/modules/communications/services/workflow-event-listener.service.ts - WorkflowExecutorService:
src/modules/communications/services/workflow-executor.service.ts - WorkflowSchedulerService:
src/modules/communications/services/workflow-scheduler.service.ts
Servicios Compartidos
- RecipientFilterService:
src/modules/communications/services/recipient-filter.service.ts - MessageDispatcherService:
src/modules/communications/services/message-dispatcher.service.ts - CommunicationTemplateService:
src/modules/communications/services/communication-template.service.ts
Modelo Prisma
- CommunicationWorkflow:
prisma/schema.prisma(modelo CommunicationWorkflow) - CommunicationCampaign:
prisma/schema.prisma(modelo CommunicationCampaign) - CommunicationMessage:
prisma/schema.prisma(modelo CommunicationMessage)
Documentación Relacionada
- Notificaciones y Comunicaciones — Arquitectura de 3 capas, canales, colas Bull, campanas masivas
- Modelo de Datos — Modelos Prisma de comunicaciones
- Seguridad — Guards y permisos para endpoints de workflows