template-saas/docs/architecture/adr/ADR-004-notifications-realtime.md
rckrdmrd 50a821a415
Some checks failed
CI / Backend CI (push) Has been cancelled
CI / Frontend CI (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / CI Summary (push) Has been cancelled
[SIMCO-V38] feat: Actualizar a SIMCO v3.8.0
- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8
- Actualizaciones de configuracion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 08:53:08 -06:00

5.8 KiB

id title type status priority supersedes superseded_by version created_date updated_date
ADR-004 Sistema de Notificaciones Real-time ADR Accepted P0 N/A N/A 1.0.0 2026-01-07 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

@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

@Processor('notifications')
export class NotificationProcessor {
  @Process('send')
  async handleSend(job: Job<NotificationJob>) {
    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)

// 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

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


Fecha decision: 2026-01-10 Autores: Claude Code (Arquitectura)