La arquitectura orientada a eventos (EDA, Event-Driven Architecture) es un estilo de diseño en el que los componentes de un sistema se comunican mediante la producción y el consumo de eventos, en lugar de invocarse directamente unos a otros. Este enfoque favorece el desacoplamiento, la escalabilidad y la capacidad de reaccionar en tiempo real a lo que ocurre en el negocio. En esta lección sentaremos las bases conceptuales que necesitarás para entender el resto del módulo: qué es un evento, en qué se diferencia de un comando y de un mensaje, qué papel juegan los productores y consumidores, y qué topologías existen para conectarlos.
Comprender bien estos fundamentos es esencial porque casi todos los sistemas modernos de gran escala (banca, e-commerce, streaming, IoT) se apoyan en eventos para integrar microservicios sin que se conozcan entre sí.
Contenido
- ¿Qué es la arquitectura orientada a eventos?
- Evento vs comando vs mensaje
- Productores y consumidores
- Topología de mediador (mediator)
- Topología de broker
- Diagrama general de flujo de eventos
- Errores comunes y consejos
- Ejercicios y soluciones
- Conclusión
- ¿Qué es la arquitectura orientada a eventos?
Un evento representa algo que ya ha ocurrido en el sistema y que es relevante para el negocio. Ejemplos: "PedidoCreado", "PagoConfirmado", "UsuarioRegistrado". Lo importante es el tiempo verbal: el evento es un hecho consumado e inmutable.
En una arquitectura orientada a eventos:
- Los componentes emiten eventos cuando cambia su estado.
- Otros componentes reaccionan a esos eventos sin que el emisor sepa quién los está escuchando.
- La comunicación es típicamente asíncrona: el emisor no espera respuesta.
Esto contrasta con la arquitectura tradicional basada en llamadas síncronas (REST/RPC), donde el cliente conoce y depende del servidor.
| Característica | Llamada síncrona (REST/RPC) | Arquitectura orientada a eventos |
|---|---|---|
| Acoplamiento | Fuerte (cliente conoce servidor) | Débil (emisor no conoce receptor) |
| Temporalidad | Bloqueante (espera respuesta) | No bloqueante (fire-and-forget) |
| Escalabilidad | Limitada por el más lento | Alta (consumidores independientes) |
| Tolerancia a fallos | Si el servidor cae, falla la llamada | El evento puede procesarse más tarde |
- Evento vs comando vs mensaje
Estos tres términos se confunden a menudo. Son tipos de mensajes, pero con intenciones diferentes.
| Concepto | Intención | Tiempo verbal | Dirección | ¿Espera respuesta? |
|---|---|---|---|---|
| Comando | Ordenar que algo ocurra | Imperativo: "CrearPedido" | 1 emisor → 1 receptor concreto | A veces |
| Evento | Notificar que algo ocurrió | Pasado: "PedidoCreado" | 1 emisor → N receptores desconocidos | No |
| Mensaje | Término genérico que engloba a ambos | — | — | Depende |
Veamos un ejemplo en código para fijar la diferencia entre comando y evento:
// COMANDO: una instrucción dirigida a un destinatario concreto.
// Se espera que ALGUIEN lo ejecute. Nombre en imperativo.
public record CrearPedidoCommand(
String clienteId,
List<LineaPedido> lineas) {
}
// EVENTO: el hecho de que el pedido YA se ha creado.
// Es inmutable, lleva un identificador y una marca de tiempo.
// Nombre en pasado.
public record PedidoCreadoEvent(
String pedidoId,
String clienteId,
BigDecimal total,
Instant ocurridoEn) {
}Explicación detallada del fragmento:
recordes una clase Java inmutable y concisa (desde Java 16), ideal para representar datos que no cambian, como los eventos.CrearPedidoCommandusa un verbo en imperativo porque expresa una intención de que algo suceda. Normalmente lo procesa un único componente responsable.PedidoCreadoEventusa el participio "Creado" porque describe un hecho pasado. IncluyeocurridoEn(unInstant, marca temporal UTC) porque todo evento debe poder ordenarse en el tiempo.- El evento contiene el
totalya calculado: transporta el resultado, no la orden de calcularlo.
Regla práctica: si puedes rechazar la petición, probablemente es un comando. Si solo estás informando de que ocurrió y nadie puede "negarse", es un evento.
- Productores y consumidores
En EDA distinguimos dos roles fundamentales:
- Productor (Producer / Publisher): el componente que detecta un cambio y emite el evento. No sabe (ni le importa) quién lo consumirá.
- Consumidor (Consumer / Subscriber): el componente que recibe el evento y reacciona (actualiza una base de datos, envía un correo, llama a otro servicio...).
// PRODUCTOR: publica el evento en un canal. No conoce a los consumidores.
@Service
public class ServicioPedidos {
private final PublicadorEventos publicador; // abstracción del canal
public ServicioPedidos(PublicadorEventos publicador) {
this.publicador = publicador;
}
public void crearPedido(CrearPedidoCommand cmd) {
// 1. Lógica de negocio: persistir el pedido
String pedidoId = UUID.randomUUID().toString();
// ... guardar en base de datos ...
// 2. Publicar el evento de lo que ha ocurrido
var evento = new PedidoCreadoEvent(
pedidoId, cmd.clienteId(), calcularTotal(cmd), Instant.now());
publicador.publicar("pedidos.creados", evento);
}
}Puntos clave:
PublicadorEventoses una abstracción: oculta si por debajo usamos Kafka, RabbitMQ, etc. Esto permite cambiar la tecnología sin tocar la lógica de negocio.- Tras guardar el pedido, el servicio publica en el canal lógico
"pedidos.creados". No hay ninguna referencia a quién escucha.
// CONSUMIDOR: se suscribe al canal y reacciona al evento.
@Component
public class NotificadorCliente {
@EventListener("pedidos.creados") // se suscribe al mismo canal
public void alCrearPedido(PedidoCreadoEvent evento) {
// Reacción: enviar confirmación al cliente
enviarEmailConfirmacion(evento.clienteId(), evento.pedidoId());
}
}- El consumidor
NotificadorClientereacciona enviando un correo. Otro consumidor (por ejemplo,ServicioFacturacion) podría escuchar el mismo evento para emitir una factura. Añadir nuevos consumidores no requiere modificar al productor: ahí reside la potencia de EDA.
- Topología de mediador (mediator)
Existen dos grandes topologías para organizar el flujo de eventos. La primera es la topología de mediador.
Aquí, un componente central (el mediador u orquestador) recibe un evento inicial, lo descompone en pasos y coordina su ejecución dirigiendo el flujo de trabajo.
- Útil cuando el proceso tiene varios pasos con orden y lógica condicional (por ejemplo, un alta de seguro: validar datos → tarificar → emitir póliza → cobrar).
- El mediador conoce el flujo completo, lo que centraliza la lógica de coordinación.
- Inconveniente: el mediador puede convertirse en un cuello de botella o en un punto único de fallo, y acopla el conocimiento del proceso en un solo lugar.
flowchart LR
A[Cliente] -->|Evento inicial| M{Mediador / Orquestador}
M -->|paso 1| S1[Servicio Validación]
M -->|paso 2| S2[Servicio Tarificación]
M -->|paso 3| S3[Servicio Emisión]
S1 -.respuesta.-> M
S2 -.respuesta.-> M
S3 -.respuesta.-> MEn el diagrama, el mediador (M) es quien decide el orden y recoge las respuestas de cada servicio. Los servicios no se conocen entre sí; solo hablan con el mediador.
- Topología de broker
La segunda es la topología de broker (mediador ausente). No hay coordinador central: los eventos fluyen libremente a través de un broker (canal/cola) y cada servicio reacciona de forma autónoma, pudiendo a su vez emitir nuevos eventos que disparan a otros.
- Ideal para flujos simples y altamente desacoplados.
- Máxima escalabilidad y resiliencia: no hay punto central.
- Inconveniente: el flujo global queda "repartido" y es más difícil de visualizar y depurar (no hay un sitio donde leer todo el proceso).
flowchart LR
P[Servicio Pedidos] -->|PedidoCreado| B((Broker))
B --> I[Servicio Inventario]
B --> F[Servicio Facturación]
I -->|StockReservado| B
F -->|FacturaEmitida| B
B --> E[Servicio Envíos]Aquí Servicio Inventario reacciona al PedidoCreado y, a su vez, emite StockReservado, que dispara al Servicio Envíos. El flujo emerge de la suma de reacciones individuales, sin coordinador.
| Criterio | Mediador | Broker |
|---|---|---|
| Control del flujo | Centralizado | Distribuido |
| Acoplamiento | Medio (todos conocen al mediador) | Mínimo |
| Visibilidad del proceso | Alta (un solo sitio) | Baja (repartido) |
| Resiliencia | Punto único de fallo | Muy alta |
| Casos ideales | Flujos complejos y condicionales | Flujos simples y desacoplados |
- Diagrama general de flujo de eventos
Combinando los conceptos, así se ve el flujo básico productor → canal → consumidores:
sequenceDiagram
participant P as Productor
participant C as Canal de eventos
participant C1 as Consumidor 1 (Email)
participant C2 as Consumidor 2 (Facturación)
P->>C: publicar(PedidoCreadoEvent)
C-->>C1: entregar evento
C-->>C2: entregar evento
Note over C1,C2: Procesan en paralelo e<br/>independientementeEl productor publica una sola vez; el canal distribuye el evento a todos los suscriptores interesados, que trabajan en paralelo y sin conocerse.
Errores Comunes y Consejos
- Confundir comandos con eventos. Si nombras un evento en imperativo ("EnviarEmail") estás disfrazando un comando. Usa siempre el pasado para eventos: "EmailSolicitado" o "PedidoCreado".
- Meter lógica de negocio del consumidor en el productor. El productor no debe saber qué hacen los consumidores. Si lo sabe, vuelves a acoplar.
- Eventos demasiado "gordos" o demasiado "flacos". Un evento debe llevar los datos suficientes para que el consumidor reaccione sin tener que llamar de vuelta al productor (lo que reintroduce acoplamiento), pero sin convertirse en un volcado de toda la base de datos.
- Olvidar la marca temporal y el identificador. Todo evento debe ser trazable e idempotentemente identificable.
- Consejo: empieza con topología de broker para flujos simples y reserva el mediador para procesos con muchos pasos condicionales.
Ejercicios
- Clasifica los siguientes mensajes como comando o evento y justifica: (a) "ReservarHabitacion", (b) "PagoRechazado", (c) "ActualizarPrecio", (d) "SesionIniciada".
- Dibuja (en pseudocódigo o palabras) cómo se vería un proceso de "alta de cliente" con topología de mediador frente a topología de broker. ¿Cuál elegirías y por qué?
- Diseña un
recordde evento en Java para "FacturaEmitida" que incluya los campos mínimos necesarios para que un consumidor de contabilidad pueda reaccionar sin consultar de vuelta al emisor.
Soluciones
- (a) Comando (imperativo, dirigido, puede rechazarse). (b) Evento (hecho pasado, notificación). (c) Comando (orden de cambiar algo). (d) Evento (la sesión ya se inició).
- Con mediador: un
OrquestadorAltaClienterecibe la petición y llama en orden a validación → creación → bienvenida, gestionando los fallos. Con broker:ServicioRegistroemiteClienteRegistrado;ServicioEmailyServicioCRMreaccionan de forma independiente. Para un alta simple, el broker es preferible por su bajo acoplamiento; si hubiera muchos pasos condicionales (verificación de identidad, scoring), el mediador facilita el control. - Una solución posible:
public record FacturaEmitidaEvent(
String facturaId,
String pedidoId,
String clienteId,
BigDecimal importeTotal,
BigDecimal impuestos,
String divisa,
Instant emitidaEn) {
}Incluye importe, impuestos y divisa para que contabilidad asiente el apunte sin consultar de nuevo.
Conclusión
Hemos visto que la arquitectura orientada a eventos sustituye las llamadas directas por la emisión y consumo de hechos del negocio, logrando un fuerte desacoplamiento. Distinguimos comandos (órdenes), eventos (hechos pasados) y mensajes (el término genérico), y entendimos los roles de productor y consumidor. Finalmente comparamos las dos topologías clave: mediador (control centralizado) y broker (máxima autonomía).
En la siguiente lección, "Mensajería Asíncrona: Colas y Brokers", profundizaremos en la infraestructura que transporta estos eventos: las colas frente a los topics, las garantías de entrega y las tecnologías concretas como RabbitMQ, Kafka y SQS.
Curso de Arquitectura de Aplicaciones
Módulo 1: Fundamentos de la Arquitectura de Aplicaciones
- ¿Qué es la Arquitectura de Aplicaciones?
- El Rol del Arquitecto de Software
- Atributos de Calidad y Requisitos No Funcionales
- Decisiones Arquitectónicas y Compromisos (Trade-offs)
- Documentación de Arquitectura: Vistas y el Modelo C4
Módulo 2: Principios y Tácticas de Diseño
- Acoplamiento, Cohesión y Separación de Responsabilidades
- Principios SOLID Aplicados a la Arquitectura
- DRY, KISS, YAGNI y Otros Principios de Diseño
- Tácticas Arquitectónicas para los Atributos de Calidad
- Gestión de la Deuda Técnica
Módulo 3: Estilos y Patrones Arquitectónicos
- Arquitectura Monolítica
- Arquitectura en Capas (N-Tier)
- Arquitectura Cliente-Servidor
- Arquitectura Hexagonal (Puertos y Adaptadores)
- Arquitectura Limpia y Cebolla (Clean & Onion)
Módulo 4: Arquitecturas Distribuidas y Microservicios
- Introducción a los Sistemas Distribuidos
- Arquitectura de Microservicios
- Descomposición de Servicios y Bounded Contexts
- API Gateway, Service Discovery y Comunicación entre Servicios
- Patrones de Resiliencia: Circuit Breaker, Retry y Bulkhead
- El Teorema CAP y la Consistencia de Datos
Módulo 5: Arquitecturas Dirigidas por Eventos y Mensajería
- Fundamentos de la Arquitectura Orientada a Eventos
- Mensajería Asíncrona: Colas y Brokers
- Patrones de Eventos: Event Sourcing y CQRS
- Gestión de Transacciones Distribuidas: Patrón Saga
- Streaming de Datos en Tiempo Real
Módulo 6: Diseño Dirigido por el Dominio (DDD)
- Conceptos Fundamentales del DDD
- Diseño Estratégico: Bounded Contexts y Lenguaje Ubicuo
- Diseño Táctico: Entidades, Agregados y Repositorios
- Mapeo de Contextos (Context Mapping)
Módulo 7: Datos y Persistencia
- Estrategias de Persistencia: SQL vs NoSQL
- Patrones de Acceso a Datos: Repository, Unit of Work y DAO
- Base de Datos por Servicio y Gestión de Datos Distribuidos
- Caché y Estrategias de Invalidación
Módulo 8: Arquitectura en la Nube y Despliegue
- Fundamentos del Cloud Computing (IaaS, PaaS, SaaS)
- Contenedores y Orquestación con Docker y Kubernetes
- Arquitectura Serverless
- Patrones de Diseño Cloud-Native
- Infraestructura como Código (IaC)
Módulo 9: Calidad, Seguridad y Observabilidad
- Escalabilidad: Horizontal vs Vertical y Balanceo de Carga
- Alta Disponibilidad y Tolerancia a Fallos
- Seguridad por Diseño y Autenticación/Autorización
- Observabilidad: Logging, Métricas y Trazabilidad
- Rendimiento y Pruebas de Carga
