template-saas/docs/97-adr/adr/ADR-004-notifications-realtime.md
Adrian Flores Cortes 806612a4db
Some checks are pending
CI / Backend CI (push) Waiting to run
CI / Frontend CI (push) Waiting to run
CI / Security Scan (push) Waiting to run
CI / CI Summary (push) Blocked by required conditions
[REESTRUCTURA-DOCS] refactor: Corregir estructura docs/ segun SIMCO-DOCUMENTACION-PROYECTO
- Renombrar 02-integraciones/ → 03-integraciones/ (resolver prefijo duplicado)
- Renombrar 02-devops/ → 04-devops/ (resolver prefijo duplicado)
- Renombrar architecture/ → 97-adr/ (agregar prefijo numerico)
- Actualizar _MAP.md con nueva estructura y version 2.1.0

Estructura final:
- 00-vision-general/
- 01-modulos/
- 02-especificaciones/
- 03-integraciones/
- 04-devops/
- 97-adr/

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 20:34:14 -06:00

239 lines
5.8 KiB
Markdown

---
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<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)
```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)