--- id: "ADR-004" title: "Sistema de Notificaciones Real-time" type: "ADR" status: "Accepted" priority: "P0" supersedes: "N/A" superseded_by: "N/A" version: "1.0.0" created_date: "2026-01-07" updated_date: "2026-01-10" --- # ADR-004: Sistema de Notificaciones Real-time ## Metadata | Campo | Valor | |-------|-------| | ID | ADR-004 | | Estado | Accepted | | Fecha | 2026-01-10 | | Supersede | N/A | ## Contexto Template SaaS necesita un sistema de notificaciones que soporte: - Múltiples canales (email, push, in-app, SMS) - Entrega en tiempo real - Preferencias de usuario - Templates reutilizables - Cola para procesamiento asíncrono - Tracking de entrega ## Opciones Consideradas ### Opción 1: Polling **Descripción:** Cliente hace requests periódicos para verificar nuevas notificaciones. **Pros:** - Simple de implementar - Sin infraestructura adicional **Contras:** - Latencia alta - Desperdicio de recursos - Mala experiencia de usuario ### Opción 2: Server-Sent Events (SSE) **Descripción:** Conexión unidireccional server→client. **Pros:** - Simple - Reconexión automática - Soportado nativamente **Contras:** - Solo unidireccional - Límite de conexiones por dominio - No ideal para bidireccional ### Opción 3: WebSocket + BullMQ ✓ **Descripción:** WebSocket para real-time + cola para procesamiento. **Pros:** - Bidireccional - Baja latencia - Cola confiable - Escalable horizontalmente - Retry automático **Contras:** - Más complejo - Requiere Redis - Manejo de reconexiones ## Decisión **Elegimos WebSocket (Socket.io) + BullMQ** porque: 1. **Real-time:** WebSocket para entrega instantánea 2. **Confiabilidad:** BullMQ para procesamiento garantizado 3. **Escalabilidad:** Redis permite múltiples instancias 4. **Multi-canal:** Cola maneja diferentes canales ## Implementación ### Arquitectura ``` ┌─────────────┐ ┌─────────────┐ │ Client │◀───▶│ WebSocket │ │ (React) │ │ Gateway │ └─────────────┘ └─────────────┘ │ ┌──────▼──────┐ │ Redis │ │ (Pub/Sub) │ └──────┬──────┘ │ ┌─────────────┐ ┌──────▼──────┐ │ BullMQ │◀───▶│ Workers │ │ Queues │ │ │ └─────────────┘ └─────────────┘ │ │ │ ┌──────▼──────┐ │ │ Email │ │ │ Push │ │ │ SMS │ └───────────▶└─────────────┘ ``` ### Canales Soportados | Canal | Provider | Uso | |-------|----------|-----| | Email | SendGrid/SES | Transaccional, marketing | | Push | Web Push API | Alertas importantes | | In-app | WebSocket | Notificaciones UI | | SMS | Twilio | (Futuro) | | WhatsApp | Meta API | (Sprint 5) | ### WebSocket Gateway ```typescript @WebSocketGateway({ namespace: '/notifications', cors: { origin: '*' }, }) export class NotificationsGateway { @SubscribeMessage('subscribe') handleSubscribe(client: Socket, payload: { tenantId: string }) { client.join(`tenant:${payload.tenantId}`); } async sendToUser(userId: string, notification: Notification) { this.server.to(`user:${userId}`).emit('notification', notification); } async sendToTenant(tenantId: string, notification: Notification) { this.server.to(`tenant:${tenantId}`).emit('notification', notification); } } ``` ### Queue Processing ```typescript @Processor('notifications') export class NotificationProcessor { @Process('send') async handleSend(job: Job) { const { notification, channels } = job.data; for (const channel of channels) { switch (channel) { case 'email': await this.emailService.send(notification); break; case 'push': await this.pushService.send(notification); break; case 'in_app': await this.gateway.sendToUser(notification.userId, notification); break; } } } } ``` ### Push Notifications (VAPID) ```typescript // Generar VAPID keys const vapidKeys = webPush.generateVAPIDKeys(); // Enviar push await webPush.sendNotification(subscription, JSON.stringify({ title: notification.title, body: notification.body, icon: '/icon.png', data: { url: notification.actionUrl }, })); ``` ### Preferencias de Usuario ```typescript interface NotificationPreferences { email: { marketing: boolean; transactional: boolean; digest: 'none' | 'daily' | 'weekly'; }; push: { enabled: boolean; alerts: boolean; updates: boolean; }; inApp: { enabled: boolean; sound: boolean; }; } ``` ## Consecuencias ### Positivas - Entrega instantánea con WebSocket - Confiabilidad con colas - Multi-canal flexible - Escalable horizontalmente - User preferences respetadas ### Negativas - Complejidad adicional - Dependencia de Redis - Costos de proveedores externos - Manejo de estados de conexión ### Monitoreo - Queue metrics (jobs pending, failed) - WebSocket connections activas - Delivery rates por canal - Error rates ## Referencias - [Socket.io Documentation](https://socket.io/docs/v4/) - [BullMQ Documentation](https://docs.bullmq.io/) - [Web Push Protocol](https://tools.ietf.org/html/rfc8030) - Implementación: `apps/backend/src/modules/notifications/` --- **Fecha decision:** 2026-01-10 **Autores:** Claude Code (Arquitectura)