Arquitectura orientada a eventos es un estilo de diseño donde los servicios anuncian hechos inmutables como eventos y los consumidores reaccionan por su cuenta. La precisión que las páginas de proveedores se saltan vive en dos palabras: un evento es un hecho inmutable, en pasado — OrderPlaced, no PlaceOrder — y al productor no le consta ni le importa quién reacciona. Esa indiferencia es la arquitectura: los consumidores se agregan, se quitan y se rompen sin que el productor cambie una línea — desacoplamiento en su sentido más literal. Y la EDA es un espectro, no una religión — un solo webhook ya es orientado a eventos; una flota de streaming también; la mayoría de los sistemas sanos son híbridos.
Puntos clave
| Pregunta | Respuesta |
|---|---|
| Un evento es | Un hecho inmutable, en pasado — los consumidores pueden reaccionar, nunca rechazarlo |
| Evento ≠ comando | Hechos transmitidos a quien le importe vs. imperativos dirigidos a un handler |
| La regla de decisión | El trabajo del productor termina antes de las reacciones → eventos; quien llama necesita la respuesta → request-response |
| Los tres sabores | Notification · event-carried state · event sourcing (una elección de almacenamiento) |
| La cuenta permanente | Consumidores idempotentes, IDs de correlación, UX de consistencia eventual |
Un hecho, muchas reacciones
// JavaScript — Cloud Code (cloud/main.js)
// Everyday EDA: the write IS the event; consumers react independently
Parse.Cloud.afterSave('Order', async (req) => {
const order = req.object;
const becamePlaced = order.get('status') === 'placed' &&
req.original?.get('status') !== 'placed';
if (!becamePlaced) return;
// Consumers of one fact — none knows about the others:
await Parse.Cloud.httpRequest({ // fulfillment system (webhook)
method: 'POST', url: process.env.FULFILL_WEBHOOK,
body: { event: 'order.placed', id: order.id },
});
await enqueueConfirmationEmail(order); // async job queue
// Live Query pushes the change to every open dashboard automatically
}); // Flutter / Dart — Back4app Flutter SDK
// The client as event consumer: subscribe to facts, react on arrival
final orders = QueryBuilder<ParseObject>(ParseObject('Order'))
..whereEqualTo('status', 'placed');
final sub = await LiveQuery().client.subscribe(orders);
sub.on(LiveQueryEvent.create, (order) => showNewOrder(order));
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. // iOS / Swift — Back4app Swift SDK
// The client as event consumer: subscribe to facts, react on arrival
let orders = Order.query("status" == "placed")
let sub = try await orders.subscribe()
sub.handleEvent { _, event in
if case .created(let order) = event { showNewOrder(order) }
}
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. // Android / Kotlin — Back4app Android SDK
// The client as event consumer: subscribe to facts, react on arrival
val orders = ParseQuery.getQuery<ParseObject>("Order")
orders.whereEqualTo("status", "placed")
val sub = ParseLiveQueryClient.Factory.getClient().subscribe(orders)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, order ->
showNewOrder(order)
}
// Nobody called this screen — it REACTED to an event, like every
// other consumer of "order placed": fulfillment, email, analytics. Un hecho — un pedido quedó realizado — y cuatro consumidores, ninguno al tanto de los demás: un sistema de fulfillment notificado por webhook, un job de email encolado, el analytics contando, dashboards actualizándose por una suscripción en vivo. Agregar un quinto consumidor no cambia nada upstream. Ese es el argumento completo, en un solo save.
El vocabulario, desenredado
El caos terminológico de los buscadores, resuelto en una tabla — tres tipos de mensaje por intención, no por transporte:
| Comando | Evento | Consulta | |
|---|---|---|---|
| Gramática | Imperativo: ShipOrder | Pasado: OrderShipped | Interrogativa |
| Audiencia | Un handler | Quien se haya suscrito | Un respondedor |
| Puede rechazarse | Sí | No — ya ocurrió | n/a |
| Acoplamiento | El emisor conoce al handler | El productor no conoce a nadie | Quien llama espera |
La advertencia de Fowler merece la cita: cuidado con el comando pasivo-agresivo — un “evento” que el productor emite exigiendo en secreto que un consumidor específico actúe. Si el flujo se rompe cuando ese consumidor no responde, era un comando desde el principio, y llamarlo evento solo escondió el acoplamiento. (Las palabras de infraestructura — broker, router, bus, canal, topic — son casi sinónimos para la caja del medio; la entrada de pub/sub cubre esa maquinaria, de la cual la EDA es la arquitectura generalizadora.)
Orientado a eventos vs. request-response
| Request-response | Orientado a eventos | |
|---|---|---|
| Acoplamiento temporal | Quien llama espera — el otro lado debe estar arriba | Fire-and-forget — los consumidores se ponen al día |
| La respuesta | Regresa a quien llamó | No hay respuesta, solo reacciones |
| Modo de falla | Error inmediato y visible | Resiliente — y eventualmente consistente |
| Debug | Una call stack | IDs de correlación a través de los saltos |
| Ideal para | Lecturas, logins, pagos — todo lo que quien llama necesita | Hechos con consumidores independientes |
La regla de una frase: si quien llama necesita el resultado para continuar, request-response; si el trabajo del productor está completo antes de que empiece cualquier reacción, eventos. Realizar el pedido necesita una respuesta síncrona (“¿funcionó?”); todo lo que viene después — email, fulfillment, analytics — son reacciones a un hecho, y por eso los sistemas reales son híbridos por diseño, no por concesión.
Los tres sabores de Fowler — y dónde encaja realmente el event sourcing
| Patrón | El evento carga | Consumidores | Intercambio |
|---|---|---|---|
| Event notification | Un ID y poco más | Llaman de vuelta por los detalles | Simple; el tráfico de callbacks reacopla |
| Event-carried state transfer | Todo el estado relevante | Mantienen copias locales | Sin callbacks; duplicación y desactualización |
| Event sourcing | Es el sistema de registro | Reconstruyen el estado por replay | Auditoría perfecta; un compromiso pesado de almacenamiento |
La desambiguación que ahorra reuniones: event sourcing es cómo un sistema almacena datos; EDA es cómo los sistemas conversan. Se componen entre sí, pero ninguno exige al otro. Una línea más que los proveedores difuminan: la entrega pub/sub olvida — los suscriptores tardíos pierden lo que se disparó antes de llegar — mientras que el streaming (el log ordenado al estilo Kafka) recuerda, dejando que los consumidores repitan desde cualquier punto; elige según la historia forme parte o no del producto.
Coreografía vs. orquestación
| Coreografía (nativa de la EDA) | Orquestación | |
|---|---|---|
| Coordinación | Los servicios reaccionan a los eventos de los demás | Un coordinador comanda cada paso |
| El flujo vive | En ningún lugar explícitamente — emergente | En un lugar visible y depurable |
| Acoplamiento | Mínimo | El orquestador conoce a todos |
| Manejo de fallas | Eventos de compensación, distribuido | Retry y lógica de error centralizados |
La regla espejo, compartida con la entrada de orquestación: orquesta cuando alguien debe ser dueño del resultado de un flujo; coreografía cuando a los productores genuinamente no les importa lo que viene después. Las transacciones de negocio largas entre servicios toman la forma de saga en ambos casos — transacciones locales encadenadas por eventos o por un orquestador, deshechas por compensaciones en vez de rollback.
EDA del día a día — sin broker
La sección que los centros de arquitectura nunca escriben: la mayoría de los desarrolladores de apps ya corre patrones orientados a eventos sin ningún broker de mensajes a la vista. Un trigger beforeSave/afterSave es un consumidor de eventos de cambio de datos. Un webhook es un evento cruzando fronteras entre empresas por HTTP puro. Una live query es pub/sub con clientes como suscriptores. Un job en segundo plano encolado por un trigger es un evento generando trabajo asíncrono. Las pestañas de código de arriba son exactamente esa pila — la escritura es el evento, el trigger es el router, y los consumidores son un webhook, una cola y una suscripción. Los brokers dedicados (Kafka, RabbitMQ, NATS — con CloudEvents como sobre estándar) justifican su peso operativo cuando el volumen de eventos, el replay y los contratos entre equipos lo exigen; la arquitectura empieza mucho antes, y “¿necesito un broker?” suele responderse solo: todavía no.
Casos de uso comunes
- Flujos de pedido y pago — un hecho, muchos departamentos: el pipeline canónico del e-commerce.
- Integración entre sistemas — servicios de equipos distintos reaccionando sin llamadas directas ni deploys compartidos.
- Funciones en tiempo real — dashboards, feeds y presencia como consumo de eventos de cara al cliente.
- Cargas con picos — colas amortiguando ráfagas para que los consumidores drenen a su propio ritmo.
- Auditoría y replay — logs de streaming donde “qué pasó, en orden” es el producto en sí.
¿Deberías adoptar la arquitectura orientada a eventos? Matriz de decisión
| Situación | Inclinación |
|---|---|
| Un hecho, varios consumidores independientes | Eventos — el partido de local |
| Quien llama necesita la respuesta para seguir | Request-response, sin pedir disculpas |
| Flujo con dueño y SLA | Orquestación — comanda los pasos |
| Reacciones dentro de una app (email al registrarse) | Triggers + jobs — EDA del día a día |
| Notificaciones entre empresas | Webhooks |
| Streams de alto volumen, con replay y multiequipo | Un broker de verdad — ahora sí se paga |
Limitaciones y trade-offs
- El debug pierde la call stack. Una solicitud que se abre en cinco saltos asíncronos solo es rastreable por IDs de correlación cargados desde el primer evento — el retrofit es doloroso; instálalos desde el primer día.
- La consistencia eventual llega a la UI. El usuario actualiza su perfil y la siguiente pantalla muestra el nombre viejo; diseña la UX para eso o mantén ese camino síncrono.
- Los duplicados son contractuales. La entrega at-least-once vuelve obligatorios los consumidores idempotentes — el mismo contrato que imponen las colas, porque es la misma maquinaria.
- Los esquemas evolucionan bajo los pies de los consumidores. Los eventos son contratos con suscriptores desconocidos: solo cambios aditivos, topics versionados para las rupturas y lectores tolerantes en todas partes.
- Los flujos emergentes se esconden. La coreografía pura significa que nadie puede señalar el proceso de negocio; cuando los auditores o la guardia lo necesiten, agrega el orquestador.
Patrones orientados a eventos en Back4app
Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. La sección de EDA del día a día es su lista de funciones leída como arquitectura: cada save es un evento, los triggers afterSave son consumidores server-side con el ecosistema JavaScript completo, las Live Queries reparten los hechos a los clientes por WebSockets gestionados, los webhooks los llevan a sistemas externos y los jobs en segundo plano absorben las reacciones lentas — las pestañas de código muestran el ciclo completo a partir de un save de pedido. Las disciplinas que este artículo exige siguen siendo tuyas (after-hooks idempotentes, pensamiento en pasado, síncrono donde quien llama necesita respuesta), mientras que la infraestructura con forma de broker — fan-out, entrega, flotas de conexiones — llega como comportamiento de plataforma, y graduarse después a un broker de streaming dedicado es una adición a esta arquitectura, no una reescritura.
Preguntas frecuentes
¿Qué es la arquitectura orientada a eventos en términos simples?
Los componentes anuncian que algo ocurrió — un evento — en vez de llamarse entre sí, y cualquier componente interesado reacciona a su propio ritmo. Transmisión de radio en vez de llamada telefónica: el trabajo de quien anuncia termina en el anuncio, y los oyentes entran y salen sin que el anunciante cambie una línea.
¿Cuál es la diferencia entre un evento y un comando?
La intención. Un comando es un imperativo dirigido a un destinatario, que puede rechazarlo — ShipOrder. Un evento es un hecho en pasado transmitido a quien le importe — OrderShipped. La trampa es el "comando pasivo-agresivo": un evento que en secreto espera que un consumidor específico actúe, es decir, un comando disfrazado de evento.
¿Cuál es la diferencia entre orientado a eventos y request-response?
El acoplamiento en el tiempo. Request-response es síncrono — quien llama espera, necesita la respuesta y falla si el otro lado está caído. Los eventos son fire-and-forget — resilientes y desacoplados, pero eventualmente consistentes. La regla: si quien llama necesita el resultado para continuar, request-response; si el trabajo del productor termina antes de que empiecen las reacciones, eventos.
¿Cuál es la diferencia entre EDA y pub/sub?
El nivel. Pub/sub es un patrón de mensajería — un mecanismo de entrega: publicadores hacia topics, topics hacia suscriptores. EDA es el estilo arquitectónico que normalmente corre sobre él, junto con colas y streams. Pub/sub es la plomería; orientado a eventos es la filosofía de diseño a la que esa plomería sirve.
¿Qué son event notification, event-carried state transfer y event sourcing?
Los tres patrones distintos de Fowler escondidos bajo un mismo nombre. Notification: eventos delgados; los consumidores llaman de vuelta por los detalles. State transfer: los eventos cargan los datos completos, y los consumidores mantienen copias locales. Sourcing: una elección de persistencia — guardar cada cambio como evento y reconstruir el estado por replay. El tercero es cómo un sistema guarda datos, no cómo los sistemas conversan.
¿Cuál es la diferencia entre coreografía y orquestación?
Coreografía: los servicios reaccionan a los eventos de los demás, sin cerebro central — desacoplamiento máximo, pero el flujo no existe explícitamente en ningún lugar. Orquestación: un coordinador comanda cada paso — visible y depurable, al costo de un punto central de acoplamiento. Regla: orquesta cuando alguien debe ser dueño del resultado; coreografía cuando a los productores genuinamente no les importa lo que viene después.
¿Cómo se manejan los eventos duplicados y fuera de orden?
Por diseño, no por esperanza: los brokers entregan at-least-once, así que los consumidores deben ser idempotentes — deduplica por ID de evento o escribe handlers cuya resolicitud sea inofensiva ("fijar el status en enviado", nunca "alternar"). El orden solo se garantiza por partición o clave; las arquitecturas que exigen orden global pelean contra su propio transporte.
¿Cuándo deberías usar arquitectura orientada a eventos?
Cuando un hecho tiene varios consumidores independientes, cuando la carga tiene picos y un buffer ayuda, cuando los sistemas deben integrarse sin conocerse, o cuando un registro de auditoría de los cambios tiene valor por sí mismo. Cuándo no: CRUD simple, lecturas, operaciones que exigen consistencia fuerte inmediata y equipos sin apetito por sistemas distribuidos.