Diagrama de Componentes - Microservicio de Notificaciones
Descripción General
El Microservicio de Notificaciones es responsable de orquestar todas las notificaciones multicanal en la plataforma Algesta. Escucha eventos de dominio de otros microservicios y envía notificaciones vía email (SendGrid), WhatsApp (Jelou) y opcionalmente notificaciones push.
El microservicio maneja:
- Activadores de notificación basados en eventos de los Microservicios de Órdenes y Proveedores
- Entrega multicanal (email, WhatsApp, push)
- Renderizado de mensajes basado en plantillas con datos dinámicos
- Historial de notificaciones y seguimiento de estado
- Mecanismo de reintento con retroceso exponencial para entregas fallidas
- Notificaciones programadas (recordatorios, alertas de expiración)
Diagrama de Componentes
graph TB
subgraph "Capa de Aplicación"
NotificationController["NotificationController<br/>[REST Controller]<br/>Endpoints de notificación manual"]
SendEmailHandler["SendEmailHandler<br/>[Command Handler]<br/>Envía notificación por email vía SendGrid"]
SendWhatsAppHandler["SendWhatsAppHandler<br/>[Command Handler]<br/>Envía mensaje de WhatsApp vía Jelou"]
SendPushHandler["SendPushHandler<br/>[Command Handler]<br/>Envía notificación push"]
ScheduleNotificationHandler["ScheduleNotificationHandler<br/>[Command Handler]<br/>Programa notificación futura"]
RetryFailedNotificationHandler["RetryFailedNotificationHandler<br/>[Command Handler]<br/>Reintenta notificación fallida"]
GetNotificationHistoryHandler["GetNotificationHistoryHandler<br/>[Query Handler]<br/>Recupera historial de notificaciones"]
GetNotificationEstadoHandler["GetNotificationEstadoHandler<br/>[Query Handler]<br/>Obtiene estado de entrega de notificación"]
OrderCreatedEventHandler["OrderCreatedEventHandler<br/>[Event Handler]<br/>Envía confirmación cuando se crea orden"]
OrderPublishedEventHandler["OrderPublishedEventHandler<br/>[Event Handler]<br/>Notifica a proveedores de nueva subasta"]
AuctionClosedEventHandler["AuctionClosedEventHandler<br/>[Event Handler]<br/>Notifica al ganador y proveedores rechazados"]
ProviderSelectedEventHandler["ProviderSelectedEventHandler<br/>[Event Handler]<br/>Notifica al proveedor de asignación"]
QuotationSentEventHandler["QuotationSentEventHandler<br/>[Event Handler]<br/>Envía cotización al cliente"]
OrderApprovedEventHandler["OrderApprovedEventHandler<br/>[Event Handler]<br/>Notifica al proveedor para iniciar ejecución"]
ExecutionReportEventHandler["ExecutionReportEventHandler<br/>[Event Handler]<br/>Envía informe de ejecución al cliente"]
DocumentoEstadoEventHandler["DocumentoEstadoEventHandler<br/>[Event Handler]<br/>Notifica al proveedor del estado del documento"]
DocumentoExpiringEventHandler["DocumentoExpiringEventHandler<br/>[Event Handler]<br/>Envía recordatorio de expiración al proveedor"]
NotificationRetryTask["NotificationRetryTask<br/>[Tarea Programada]<br/>Reintenta notificaciones fallidas cada hora"]
ExpiryReminderTask["ExpiryReminderTask<br/>[Tarea Programada]<br/>Envía recordatorios de expiración diarios"]
end
subgraph "Capa de Dominio"
NotificationEntity["Notification Entity<br/>[Aggregate Root]<br/>Modelo de dominio de notificación"]
NotificationTemplateVO["NotificationTemplate<br/>[Objeto de Valor]<br/>Plantilla con marcadores de posición"]
NotificationChannelEnum["NotificationChannel<br/>[Enum]<br/>EMAIL, WHATSAPP, PUSH"]
NotificationEstadoEnum["NotificationEstado<br/>[Enum]<br/>PENDING, SENT, FAILED, RETRYING"]
NotificationRepositorio["NotificationRepositorio<br/>[Interface]<br/>Abstracción de repositorio"]
end
subgraph "Capa de Infraestructura"
NotificationRepositorioImpl["NotificationRepositorioImpl<br/>[Implementación Mongoose]<br/>Persistencia MongoDB para notificaciones"]
SendGridEmailService["SendGridEmailService<br/>[Servicio Externo]<br/>Entrega de email vía SendGrid API"]
WhatsAppService["WhatsAppService<br/>[Servicio Externo]<br/>Entrega de WhatsApp vía Jelou API"]
PushNotificationService["PushNotificationService<br/>[Servicio Externo]<br/>Entrega de notificación push"]
TemplateRenderingService["TemplateRenderingService<br/>[Servicio de Dominio]<br/>Renderiza plantillas con Handlebars"]
MessageBrokerService["MessageBrokerService<br/>[Suscriptor de Eventos]<br/>Se suscribe a eventos de Redis/Kafka"]
end
subgraph "Dependencias Externas"
MongoDB[("MongoDB<br/>[Base de datos]<br/>Base de datos de notificaciones")]
SendGridAPI["SendGrid API<br/>[Sistema Externo]<br/>Servicio de entrega de email"]
JelouAPI["Jelou API<br/>[Sistema Externo]<br/>Plataforma de bot de WhatsApp"]
RedisKafka[("Redis/Kafka<br/>[Message Broker]<br/>Transmisión de eventos")]
end
NotificationController --> SendEmailHandler
NotificationController --> SendWhatsAppHandler
NotificationController --> SendPushHandler
NotificationController --> ScheduleNotificationHandler
NotificationController --> GetNotificationHistoryHandler
NotificationController --> GetNotificationEstadoHandler
SendEmailHandler --> NotificationEntity
SendEmailHandler --> NotificationRepositorio
SendEmailHandler --> SendGridEmailService
SendEmailHandler --> TemplateRenderingService
SendWhatsAppHandler --> NotificationEntity
SendWhatsAppHandler --> NotificationRepositorio
SendWhatsAppHandler --> WhatsAppService
SendWhatsAppHandler --> TemplateRenderingService
SendPushHandler --> NotificationEntity
SendPushHandler --> NotificationRepositorio
SendPushHandler --> PushNotificationService
ScheduleNotificationHandler --> NotificationRepositorio
RetryFailedNotificationHandler --> NotificationRepositorio
RetryFailedNotificationHandler --> SendEmailHandler
RetryFailedNotificationHandler --> SendWhatsAppHandler
GetNotificationHistoryHandler --> NotificationRepositorio
GetNotificationEstadoHandler --> NotificationRepositorio
OrderCreatedEventHandler --> SendEmailHandler
OrderCreatedEventHandler --> SendWhatsAppHandler
OrderPublishedEventHandler --> NotificationRepositorio
OrderPublishedEventHandler --> SendEmailHandler
AuctionClosedEventHandler --> SendEmailHandler
AuctionClosedEventHandler --> SendWhatsAppHandler
ProviderSelectedEventHandler --> SendEmailHandler
ProviderSelectedEventHandler --> SendWhatsAppHandler
QuotationSentEventHandler --> SendEmailHandler
QuotationSentEventHandler --> SendWhatsAppHandler
OrderApprovedEventHandler --> SendEmailHandler
OrderApprovedEventHandler --> SendWhatsAppHandler
ExecutionReportEventHandler --> SendEmailHandler
ExecutionReportEventHandler --> SendWhatsAppHandler
DocumentoEstadoEventHandler --> SendEmailHandler
DocumentoEstadoEventHandler --> SendWhatsAppHandler
DocumentoExpiringEventHandler --> SendEmailHandler
DocumentoExpiringEventHandler --> SendWhatsAppHandler
NotificationRetryTask --> NotificationRepositorio
NotificationRetryTask --> RetryFailedNotificationHandler
ExpiryReminderTask --> NotificationRepositorio
MessageBrokerService --> OrderCreatedEventHandler
MessageBrokerService --> OrderPublishedEventHandler
MessageBrokerService --> AuctionClosedEventHandler
MessageBrokerService --> ProviderSelectedEventHandler
MessageBrokerService --> QuotationSentEventHandler
MessageBrokerService --> OrderApprovedEventHandler
MessageBrokerService --> ExecutionReportEventHandler
MessageBrokerService --> DocumentoEstadoEventHandler
MessageBrokerService --> DocumentoExpiringEventHandler
MessageBrokerService --> RedisKafka
NotificationRepositorio --> NotificationRepositorioImpl
NotificationRepositorioImpl --> MongoDB
SendGridEmailService --> SendGridAPI
WhatsAppService --> JelouAPI
style NotificationController fill:#438dd5,stroke:#2e6295,color:#ffffff
style SendEmailHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
style SendWhatsAppHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
style SendPushHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
style ScheduleNotificationHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
style RetryFailedNotificationHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
style GetNotificationHistoryHandler fill:#a8d5ba,stroke:#7cb99a,color:#000000
style GetNotificationEstadoHandler fill:#a8d5ba,stroke:#7cb99a,color:#000000
style OrderCreatedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style OrderPublishedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style AuctionClosedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style ProviderSelectedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style QuotationSentEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style OrderApprovedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style ExecutionReportEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style DocumentoEstadoEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style DocumentoExpiringEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
style NotificationRetryTask fill:#ff9ff3,stroke:#cc7fc2,color:#000000
style ExpiryReminderTask fill:#ff9ff3,stroke:#cc7fc2,color:#000000
style NotificationEntity fill:#6bcf7f,stroke:#56a466,color:#ffffff
style NotificationRepositorioImpl fill:#ff6b6b,stroke:#cc5555,color:#ffffff
style MongoDB fill:#ff6b6b,stroke:#cc5555,color:#ffffff
style SendGridAPI fill:#999999,stroke:#6b6b6b,color:#ffffff
style JelouAPI fill:#999999,stroke:#6b6b6b,color:#ffffff
style RedisKafka fill:#48dbfb,stroke:#2e9cba,color:#000000
Figura: Diagrama de Componentes del Microservicio de Notificaciones - Orquestación de notificaciones basada en eventos con entrega multicanal vía email y WhatsApp
Notification Flow Sequence Diagram
Event-Driven Notification Example: Order Created
sequenceDiagram
participant OrdersMS as Orders MS
participant Broker as Redis/Kafka
participant EventHandler as OrderCreatedEventHandler
participant TemplateService as TemplateRenderingService
participant EmailHandler as SendEmailHandler
participant WhatsAppHandler as SendWhatsAppHandler
participant SendGrid as SendGrid API
participant Jelou as Jelou API
participant DB as MongoDB
OrdersMS->>Broker: publish(OrderCreatedEvent)
Broker->>EventHandler: OrderCreatedEvent received
EventHandler->>TemplateService: render("order_created_email", orderData)
TemplateService-->>EventHandler: Rendered email HTML
EventHandler->>EmailHandler: execute(SendEmailCommand)
EmailHandler->>SendGrid: send email to client
SendGrid-->>EmailHandler: success
EmailHandler->>DB: save notification (Estado: SENT, channel: EMAIL)
EmailHandler-->>EventHandler: email sent
EventHandler->>TemplateService: render("order_created_whatsapp", orderData)
TemplateService-->>EventHandler: Rendered WhatsApp message
EventHandler->>WhatsAppHandler: execute(SendWhatsAppCommand)
WhatsAppHandler->>Jelou: send message to client
Jelou-->>WhatsAppHandler: success
WhatsAppHandler->>DB: save notification (Estado: SENT, channel: WHATSAPP)
WhatsAppHandler-->>EventHandler: WhatsApp sent
Note over EventHandler: Both notifications sent successfully
Figura: Order Created Notification Flow - Event-driven notification example showing multi-channel delivery (email + WhatsApp) triggered by order creation event
Stack Tecnológico
| Dependency | Version | Purpose |
|---|---|---|
@nestjs/core | 11.x | NestJS framework core |
@nestjs/cqrs | 11.x | CQRS pattern Implementación |
@nestjs/mongoose | 11.x | MongoDB integration via Mongoose |
@nestjs/Microservicios | 11.x | Redis/Kafka client for messaging |
@nestjs/schedule | 4.x | Scheduled tasks for retries and reminders |
mongoose | 8.x | MongoDB ODM |
@sendgrid/mail | 8.x | SendGrid email API client |
axios | 1.x | HTTP client for Jelou API |
handlebars | 4.x | Template rendering engine |
class-validator | 0.14.x | DTO validation |
class-transformer | 0.5.x | DTO transformation |
Key Design Patterns
Event-Driven Arquitectura
The Notifications MS is purely event-driven, listening to events from other Microservicios:
Subscribed Events:
OrderCreatedEvent- Send confirmation to clientOrderPublishedEvent- Notify eligible providers of new auctionAuctionClosedEvent- Notify winner and rejected providersProviderSelectedEvent- Notify provider of assignmentQuotationSentToClientEvent- Send quotation to clientOrderApprovedEvent- Notify provider to start executionExecutionReportSubmittedEvent- Send execution report to clientDocumentoEstadoChangedEvent- Notify provider of Documento approval/rejectionDocumentoExpiringEvent- Send expiry reminder to providerDocumentoExpiredEvent- Notify provider of expired Documento
Each event handler is responsible for:
- Receiving the event from message broker
- Determining recipients (client, provider, agent)
- Selecting appropriate templates
- Rendering templates with event data
- Dispatching notifications via appropriate channels
- Recording notification history
Template Pattern for Notifications
Templates are stored as Handlebars templates with placeholders:
<!DOCTYPE html><html><head> <title>Order Confirmation - Algesta</title></head><body> <h1>Order Created Successfully</h1> <p>Dear {{clientName}},</p> <p>Your order #{{orderId}} has been created successfully.</p> <h2>Order Details:</h2> <ul> <li>Service Type: {{serviceType}}</li> <li>Description: {{description}}</li> <li>Location: {{location}}</li> <li>Priority: {{priority}}</li> </ul> <p>We will notify you once a provider is assigned.</p> <p>Thank you for choosing Algesta!</p></body></html>🎉 *Orden Creada Exitosamente*
Hola {{clientName}},
Tu orden #{{orderId}} ha sido creada.
*Detalles:*- Servicio: {{serviceType}}- Descripción: {{description}}- Ubicación: {{location}}- Prioridad: {{priority}}
Te notificaremos cuando se asigne un proveedor.
¡Gracias por elegir Algesta!Template Rendering Service:
@Injectable()export class TemplateRenderingService { private handlebars = Handlebars.create();
async renderTemplate(templateName: string, data: any): Promise<string> { const templatePath = path.join(__dirname, 'templates', `${templateName}.hbs`); const templateSource = await fs.readFile(templatePath, 'utf-8'); const template = this.handlebars.compile(templateSource); return template(data); }}Retry Pattern with Exponential Backoff
Failed notifications are retried with exponential backoff:
@Injectable()export class NotificationRetryTask { private readonly maxRetries = 3; private readonly baseDelay = 1000; // 1 second
@Cron('0 * * * *') // Every hour async retryFailedNotifications() { const failedNotifications = await this.notificationRepository.findByStatus('FAILED');
for (const notification of failedNotifications) { if (notification.retryCount < this.maxRetries) { const delay = this.calculateBackoff(notification.retryCount); await this.delay(delay);
try { await this.commandBus.execute( new RetryFailedNotificationCommand(notification.id) ); notification.status = 'SENT'; notification.retryCount++; } catch (error) { notification.retryCount++; if (notification.retryCount >= this.maxRetries) { notification.status = 'FAILED_PERMANENTLY'; } }
await this.notificationRepository.update(notification); } } }
private calculateBackoff(retryCount: number): number { return this.baseDelay * Math.pow(2, retryCount); // 1s, 2s, 4s }
private delay(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); }}Scheduled Tasks for Background Processing
NotificationRetryTask:
- Runs hourly (via
@Cron('0 * * * *')) - Queries failed notifications
- Retries with exponential backoff
- Marks as permanently failed after max retries
ExpiryReminderTask:
- Runs daily at midnight
- Queries scheduled notifications for the day
- Sends expiry reminders (30, 15, 7 days before)
- Sends other scheduled notifications
Notification Data Model
interface Notification { id: string; channel: NotificationChannel; // EMAIL, WHATSAPP, PUSH status: NotificationStatus; // PENDING, SENT, FAILED, RETRYING, FAILED_PERMANENTLY recipientType: 'CLIENT' | 'PROVIDER' | 'AGENT'; recipientId: string; recipientEmail?: string; recipientPhone?: string; subject?: string; // For email message: string; templateName: string; templateData: object; sentAt?: Date; failedAt?: Date; errorMessage?: string; retryCount: number; maxRetries: number; relatedEntityType: 'ORDER' | 'PROVIDER' | 'AUCTION' | 'DOCUMENT'; relatedEntityId: string; createdAt: Date; updatedAt: Date;}Integration Points
SendGrid Integration
Email Delivery:
@Injectable()export class SendGridEmailService { constructor( @Inject('SENDGRID_API_KEY') private apiKey: string, ) { sgMail.setApiKey(this.apiKey); }
async sendEmail(to: string, subject: string, html: string): Promise<void> { const msg = { to, from: 'notifications@algesta.com', subject, html, };
try { await sgMail.send(msg); } catch (error) { throw new EmailDeliveryException(error.message); } }}Jelou Integration
WhatsApp Delivery:
@Injectable()export class WhatsAppService { constructor( private readonly httpService: HttpService, @Inject('JELOU_API_URL') private jelouApiUrl: string, @Inject('JELOU_API_KEY') private jelouApiKey: string, ) {}
async sendMessage(phone: string, message: string): Promise<void> { const url = `${this.jelouApiUrl}/v1/messages`; const headers = { 'Authorization': `Bearer ${this.jelouApiKey}`, 'Content-Type': 'application/json', }; const body = { phone, message, type: 'text', };
try { await this.httpService.post(url, body, { headers }).toPromise(); } catch (error) { throw new WhatsAppDeliveryException(error.message); } }}Referencias Cruzadas
- Detailed Implementación: Notifications Microservicio (detailed Implementación)
- Arquitectura Descripción General: Backend Microservicios Descripción General
- Inter-Service Communication: Inter-Service Communication
- Jelou WhatsApp Integration: Jelou WhatsApp Integration
- Data Flow Diagrams: All data flow diagrams include notification steps