Tu API no necesita más servicios, necesita eventos
Si cada nueva funcionalidad te obliga a modificar cinco servicios distintos, probablemente tengas un problema de acoplamiento. Aprende cómo Event-Driven Architecture ayuda a desacoplar módulos y escalar aplicaciones NestJS.

Llega un momento en toda aplicación en crecimiento donde un simple caso de uso empieza a convertirse en una cadena de responsabilidades difícil de mantener.
Imagina el proceso de registro de usuarios.
Al principio parece sencillo:
await userRepository.create(user);
Meses después, el mismo flujo termina haciendo algo parecido a esto:
await userRepository.create(user);
await emailService.sendWelcomeEmail(user);
await auditService.registerUserCreation(user);
await analyticsService.trackNewUser(user);
await crmService.createContact(user);
await notificationService.notifyAdmins(user);
El problema no es el código.
El problema es que tu módulo de usuarios ahora conoce demasiadas cosas.
Sabe cómo enviar correos.
Sabe cómo registrar auditorías.
Sabe cómo actualizar métricas.
Sabe cómo integrarse con sistemas externos.
Cada nueva integración aumenta el acoplamiento.
Cada nuevo requerimiento obliga a modificar código que ya funcionaba.
Y eventualmente algo tan simple como registrar un usuario se convierte en una operación de alto riesgo.
Aquí es donde entra Event-Driven Architecture.
El problema real: dependencias invisibles
Cuando analizamos sistemas que se vuelven difíciles de mantener, normalmente encontramos el mismo patrón:
UserService
├── EmailService
├── AuditService
├── AnalyticsService
├── CRMService
└── NotificationService
Cada dependencia adicional aumenta:
- Complejidad
- Acoplamiento
- Tiempo de pruebas
- Riesgo de regresiones
La consecuencia es que un cambio aparentemente pequeño termina afectando múltiples áreas del sistema.
La idea detrás de Event-Driven Architecture
En lugar de ejecutar todas las acciones directamente, el módulo publica un evento.
UserService
│
▼
UserCreatedEvent
│
┌────┼────┬─────┐
▼ ▼ ▼ ▼
Email Audit CRM Analytics
El servicio ya no necesita conocer quién consume el evento.
Solo comunica algo que ocurrió.
this.eventBus.publish(
new UserCreatedEvent(user.id, user.email),
);
Nada más.
Los eventos representan hechos
Un error común es modelar eventos como comandos.
Mal:
SendWelcomeEmailEvent
Bien:
UserCreatedEvent
La diferencia es importante.
Los eventos describen algo que ya ocurrió.
No expresan una intención.
Piensa en ellos como hechos del negocio.
UserCreatedEvent
OrderPaidEvent
InvoiceGeneratedEvent
SubscriptionCanceledEvent
Implementándolo en NestJS
NestJS incluye soporte nativo para eventos mediante CQRS.
Definimos el evento:
export class UserCreatedEvent {
constructor(
public readonly userId: string,
public readonly email: string,
) {}
}
Publicamos el evento:
this.eventBus.publish(
new UserCreatedEvent(
user.id,
user.email,
),
);
Creamos un handler:
@EventsHandler(UserCreatedEvent)
export class SendWelcomeEmailHandler
implements IEventHandler<UserCreatedEvent>
{
async handle(event: UserCreatedEvent) {
await this.emailService.sendWelcomeEmail(
event.email,
);
}
}
Ahora el módulo de usuarios ya no sabe nada sobre correos electrónicos.
Un evento, múltiples consumidores
Esta es una de las ventajas más importantes.
Un mismo evento puede ser consumido por varios módulos.
UserCreatedEvent
├── SendWelcomeEmailHandler
├── RegisterAuditHandler
├── SyncCRMHandler
└── GenerateMetricsHandler
Y el productor no necesita modificarse.
¿Cuándo usar eventos?
No todo debe convertirse en un evento.
Los eventos son especialmente útiles cuando:
- Existen efectos secundarios.
- Hay múltiples consumidores.
- Los módulos evolucionan de forma independiente.
- El proceso puede ejecutarse de forma asíncrona.
Por ejemplo:
✅ Registro de usuarios
✅ Creación de órdenes
✅ Generación de facturas
✅ Actualización de inventario
✅ Notificaciones
¿Cuándo NO usar eventos?
Si una operación es estrictamente necesaria para completar una transacción:
CreateOrder
└── ValidateStock
No la conviertas en evento.
Forma parte del flujo principal.
Los eventos son excelentes para desacoplar.
No son una excusa para ocultar lógica crítica.
Lo que ganamos en la práctica
| Beneficio | Impacto |
|---|---|
| Menor acoplamiento | Menos cambios cruzados |
| Escalabilidad | Nuevos consumidores sin tocar el productor |
| Mantenibilidad | Código más simple |
| Reutilización | Un evento alimenta múltiples procesos |
| Evolución | Integraciones independientes |
Un patrón que encaja perfectamente con Arquitectura Hexagonal
Si ya aplicas Arquitectura Hexagonal, los eventos encajan de forma natural.
El dominio genera eventos.
La aplicación los publica.
Los adaptadores reaccionan.
Esto mantiene el núcleo de negocio limpio y libre de dependencias externas.
Para cerrar
Muchos equipos intentan resolver problemas de escalabilidad agregando más servicios.
La mayoría de las veces el problema no es la cantidad de servicios.
Es la cantidad de dependencias entre ellos.
Event-Driven Architecture permite que los módulos colaboren sin conocerse.
Y cuando un sistema deja de depender de llamadas directas para cada integración, la velocidad de desarrollo aumenta y el mantenimiento deja de convertirse en una pesadilla.
Si tu UserService envía correos, registra auditorías, actualiza métricas y sincroniza sistemas externos, probablemente no necesitas otro servicio.
Probablemente necesitas eventos.
Recibe artículos por email
Sin spam — solo un aviso cuando publique algo nuevo sobre backend, cloud y arquitectura.
Un email cuando salga un artículo. Puedes darte de baja cuando quieras.
Artículos relacionados

Cómo construir una aplicación SaaS Multi-Tenant en NestJS sin duplicar tu código
Si agregar un nuevo cliente implica desplegar una nueva aplicación o copiar una base de datos completa, probablemente tu arquitectura SaaS no está preparada para escalar. Aprende cómo implementar multi-tenancy en NestJS de forma limpia y mantenible

CQRS en NestJS: deja de mezclar lecturas y escrituras en el mismo servicio
Cuando tu UserService atiende POST y GET, optimizar un lado rompe el otro. Comandos, consultas y handlers con @nestjs/cqrs, sin humo.

Por qué tu servicio NestJS se vuelve inmantenible (y cómo la arquitectura hexagonal lo arregla)
Controladores que saben demasiado, entidades llenas de decoradores ORM y tests que exigen base de datos. Guía práctica de puertos y adaptadores en NestJS.