Communication Workflows
Documento tecnico que describe el sistema de workflows automatizados para comunicaciones en Nvito. Los workflows permiten configurar envios automaticos de mensajes (email, WhatsApp, SMS, push) en respuesta a eventos del dominio o en momentos programados relativos a la fecha del evento.
| Campo | Valor |
|---|---|
| Version | 1.0 |
| Fecha | Marzo 2026 |
| Estado | En revision |
| Audiencia | Equipo de desarrollo, arquitectos, stakeholders |
Tabla de Contenidos
- Modelo CommunicationWorkflow
- Controller y Servicio
- Tipos de Triggers
- Flujo de Ejecucion
- Filtrado de Destinatarios
- Integracion con Sistema de Notificaciones
- Retry Policy y Ciclo de Vida de Mensajes
- Limites y Restricciones
- Testing
- Referencias
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 especifico y se ejecuta automaticamente cuando se cumple su condicion de trigger.
Campos del Modelo
| Campo | Tipo | Descripcion |
|---|---|---|
id | UUID | Identificador unico del workflow |
eventId | UUID | Evento al que pertenece el workflow |
name | string | Nombre descriptivo del workflow (ej: "Recordatorio 7 dias antes") |
trigger | enum | Evento que dispara la ejecucion |
triggerConfig | JSON | Configuracion del trigger (ej: { daysOffset: 7 }) |
campaignType | enum | Tipo de campana que se crea al ejecutarse |
channel | string | Canal de comunicacion (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 comunicacion 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 ultima ejecucion |
createdAt | datetime | Fecha de creacion |
updatedAt | datetime | Ultima modificacion |
CommunicationWorkflowStatus
| Estado | Descripcion |
|---|---|
ACTIVE | El workflow se ejecutara cuando ocurra su trigger |
PAUSED | El workflow esta 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 | Categoria | Descripcion |
|---|---|---|
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 invitacion |
DAYS_BEFORE_EVENT | Programado | Se ejecuta N dias antes de la fecha del evento |
EVENT_DAY | Programado | Se ejecuta el dia del evento |
POST_EVENT | Programado | Se ejecuta despues 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 | Descripcion | Validaciones |
|---|---|---|---|
GET | /communication-workflows?eventId=xxx | Listar workflows del evento | eventId requerido, acceso al evento |
POST | /communication-workflows | Crear workflow | Maximo 10 por evento, trigger valido, channel valido |
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 logica de negocio para la gestion 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 ejecucion actual se completa pero no se generaran nuevas.
Tipos de Triggers
Los triggers se dividen en dos categorias fundamentales segun su mecanismo de activacion: 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 util para enviar automaticamente la invitacion 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 invitacion. Permite enviar automaticamente un mensaje de confirmacion con detalles del evento (ubicacion, horario, dress code). El destinatario es el invitado que confirmo.
RSVP_DECLINED: Se ejecuta cuando un invitado declina la invitacion. 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 especificos 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 algun workflow programado debe dispararse ese dia.
DAYS_BEFORE_EVENT: Requiere triggerConfig.daysOffset (numero entero positivo). El workflow se ejecuta N dias 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 mas comun para recordatorios.
EVENT_DAY: Se ejecuta el mismo dia del evento. No requiere configuracion adicional. Es util para enviar un mensaje de "nos vemos hoy" con los ultimos detalles logisticos (direccion, estacionamiento, etc.). Despues de ejecutarse, el workflow transiciona automaticamente a COMPLETED.
POST_EVENT: Se ejecuta despues del evento. Puede configurarse con triggerConfig.daysOffset para especificar cuantos dias despues. Es util para mensajes de agradecimiento, encuestas de satisfaccion o compartir fotos de la galeria. Despues 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 | Descripcion |
|---|---|---|
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 numero de telefono |
hasEmail | Solo invitados con email | |
hasPushToken | Push | Solo invitados con token de push registrado |
Flujo de Ejecucion
El flujo de ejecucion de un workflow sigue una cadena de servicios con responsabilidades bien definidas. El flujo varia segun el tipo de trigger.
Flujo para Triggers de Evento de Dominio
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 | Orquesta la ejecucion de un workflow: obtiene destinatarios, invoca el dispatcher, actualiza contadores. Es la pieza central que conecta la deteccion 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 multiples criterios de filtrado con logica 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 categorias definidas por el organizador (familia del novio, amigos de la novia, companeros 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 automaticamente 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.
Integracion 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
Capa 1 — NotificationChannelsModule (canales puros): Servicios de envio sin logica de negocio. Cada servicio sabe como enviar un mensaje por su canal especifico. 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 publica que los workflows (y otros modulos) consumen. Maneja la logica de creacion 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:
Estados del Mensaje
| Estado | Descripcion | 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 confirmacion |
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 despues 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 | Descripcion |
|---|---|---|
| Intentos maximos | 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 maximo | 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 mas. 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 maximos por evento | 10 | Evitar complejidad excesiva y conflictos entre workflows |
| Ejecuciones simultaneas | 1 por workflow | Un workflow no puede ejecutarse si ya tiene una ejecucion en progreso |
| Destinatarios por ejecucion | 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 organizacion.
- 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 posicion 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 multiples 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)
- Validacion de limite de 10 workflows por evento
- Toggle entre ACTIVE y PAUSED
- Rechazo de toggle en workflows COMPLETED
CommunicationWorkflowsController:
- Autorizacion: requiere token valido y acceso al evento
- Validacion de DTOs con class-validator
- Respuestas correctas para cada endpoint
WorkflowEventListenerService:
- Deteccion correcta de triggers por tipo de evento de dominio
- Filtrado de workflows ACTIVE (ignora PAUSED y COMPLETED)
- Manejo de multiples workflows para el mismo trigger
WorkflowExecutorService:
- Ejecucion completa del flujo (filtrado -> dispatch -> contadores)
- Incremento de executionCount y lastExecutedAt
- Transicion a COMPLETED para triggers de ejecucion unica
RecipientFilterService:
- Filtrado por rsvpStatus, guestGroup, tags
- Exclusion de destinatarios sin datos de contacto
- Combinacion AND de multiples filtros
MessageDispatcherService:
- Creacion 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)
Documentacion 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