Event-Driven Architecture
Qué hace que una arquitectura sea event-driven
Event-Driven Architecture (EDA) organiza interacción, reacción o propagación de información mediante eventos que describen ocurrencias relevantes o cambios de estado y tienen significancia arquitectónica. La publicación de hechos y las responsabilidades de quienes reaccionan condicionan contratos, dependencias y evolución. Registrar algunos eventos técnicos no basta para caracterizar el conjunto.
Las fuentes describen alcances diferentes. Hohpe presenta una realización con broadcast, publicación oportuna y procesamiento asíncrono, y analiza qué obligaciones aparecen al dejar de depender del call stack. Es un marco concreto, no una lista de requisitos para todo uso del término. Programming Without a Call Stack, 2006.
Aquí se examina un paquete de decisiones: qué hechos se publican, quién es autoridad, cómo se transportan y qué significa reaccionar correctamente. No se exige que toda comunicación sean eventos, ni broker, Kafka, Microservices, deployment distribuido o Event Sourcing. Eventos significativos pueden conectar módulos dentro de un proceso o sistemas a través de una red.
El Atlas representa EDA como architecture-profile, con kind: architecture-profile. Una fuente puede llamarla estilo, arquitectura o approach; su denominación permanece válida dentro de su marco. architectural-style-vs-architecture-profile explica por qué esa representación editorial y architectural-style son compatibles.
Event, Command y Message
Un evento normalmente expresa algo que ocurrió: OrderPlaced, PaymentAuthorized, InventoryReserved o ShipmentCreated. Un comando solicita o intenta una acción: AuthorizePayment. La autoridad que acepta el comando puede rechazarlo; anunciar PaymentAuthorized expresa que ya se alcanzó un hecho bajo su contrato. EIP distingue Command Message y Event Message.
Cambiar un nombre a pasado no transforma una solicitud en hecho. Si Payments debe ejecutar una acción específica y el emisor espera esa obligación, el contrato necesita expresarla. Las plataformas y autores usan event de maneras diversas; importa aclarar intención y significado.
Event describe semántica; message describe la representación transportada. Un mensaje puede llevar comando, evento, consulta, resultado o notificación. CloudEvents distingue ocurrencia, registro del evento, contexto y transporte; estandariza una envoltura interoperable, sin definir toda EDA ni imponer su adopción. CloudEvents 1.0.2, Terminology. En integración, EIP llama Event Message al evento comunicado: ambas lecturas son compatibles.
reliable-messaging desarrolla entrega y recuperación de esas representaciones. Mensajería confiable soporta EDA, pero también sistemas orientados a comandos. Las obligaciones de transporte y efecto no determinan por sí solas la organización arquitectónica.
Mecanismos y espera
Un broker puede distribuir hechos, ejecutar work queues, transportar comandos o implementar request/reply. Usar Kafka, RabbitMQ o Service Bus no determina la semántica del sistema.
EDA puede utilizar dispatch en proceso, broker, event stream, webhook u otros connectors. Pub/Sub es un modelo de interacción que permite múltiples suscriptores; no convierte cualquier mensaje publicado en evento ni es el único mecanismo posible. EIP, Publish-Subscribe Channel.
Procesar eventos asíncronamente es frecuente, pero semántica y espera pertenecen a dimensiones distintas. Encolar AuthorizePayment sigue siendo enviar un comando. Un handler de evento puede realizar llamadas síncronas; un dispatcher local puede invocar handlers antes de retornar. communication y execution-model ayudan a declarar qué continuación espera y cómo vive el trabajo. La aceptación del envío tampoco representa finalización del proceso de negocio.
Event Notification y Event-Carried State Transfer
Fowler distingue dos formas de comunicar cambios. What do you mean by Event-Driven?.
| Aspecto | Event Notification | Event-Carried State Transfer |
|---|---|---|
| Ejemplo | OrderChanged(orderId). | OrderUpdated(orderId, status, customerId, total). |
| Información | Señala un cambio con contexto mínimo. | Incluye estado relevante para el receptor. |
| Continuación | Puede requerir consultar Orders. | Puede actualizar una vista local. |
| Beneficio posible | Mensaje pequeño. | Menos callbacks. |
| Costo | Dependencia y carga de consulta. | Copias, frescura y evolución del contrato. |
Ambas formas pueden ser event-driven. La diferencia es cuánto recibe el consumidor y qué interacción posterior necesita, no síncrono frente a asíncrono. La segunda puede reducir consultas y añadir responsabilidad de mantener una copia; no es universalmente superior.
Event Sourcing, CQRS y replay
Event Sourcing utiliza una historia de eventos como representación autoritativa desde la que deriva o reconstruye estado. EDA enfatiza interacción y reacción: puede conservar estado mutable convencional y publicar hechos sin usar Event Sourcing. Un componente event-sourced también puede mantener su historia interna sin convertir toda integración en EDA. Fowler, Event Sourcing.
CQRS separa modelos de escritura y lectura. Puede combinarse con EDA, pero ninguna exige la otra; CQRS tampoco implica Event Sourcing. Fowler, CQRS. Son decisiones distintas, aunque aparezcan juntas en ejemplos.
Algunos canales conservan eventos y permiten replay. EDA no garantiza historia completa ni reproducibilidad: hacen falta retención, versiones interpretables y control de efectos. Reconstruir una proyección no debe volver a cobrar o enviar correos inadvertidamente. Persistir un evento en un log transportado tampoco convierte ese log en fuente autoritativa.
Un hecho publicado no debería reinterpretarse retrospectivamente sin un contrato explícito. Las correcciones necesitan significado propio; retención, eliminación y almacenamiento son decisiones adicionales. Evento sobre el pasado no significa existencia eterna.
Productores, consumidores y coupling
El productor publica según un contrato; el consumidor reacciona actualizando estado, iniciando trabajo, notificando o coordinando. Según la realización, un evento puede tener cero, uno o muchos consumidores. El productor puede desconocer sus identidades sin dejar de depender del canal acordado.
La mensajería durable puede reducir la necesidad de disponibilidad simultánea. Esa separación temporal depende de persistencia, retención y entrega; dispatch en memoria no conserva trabajo después de perder el proceso. Event Notification puede reintroducir dependencia runtime cuando un consumidor consulta al productor.
Hohpe distingue coupling temporal, ubicación, topología y formato o significado. Añadir suscriptores sin modificar el productor no elimina dependencias semánticas ni costos de cambio de los contratos. Event-driven = Loosely coupled? Not so fast!. dependency-structure permite analizar qué cambios se propagan.
Contratos, límites y ownership
El contrato necesita nombre, significado, schema, autoridad productora, compatibilidad y evolución de versiones. PaymentAuthorized debe aclarar identidad, importe, moneda y transición que acredita; mismo JSON no demuestra interoperability. Topic y schema pueden ser parte de interfaces, sin completar su semántica.
data-ownership conserva autoridad: Payments define el ciclo de vida del pago y publica sus hechos. Orders puede mantener una copia para decidir, pero no se convierte en autoridad sobre esa información. Copias deliberadas añaden frescura, reconciliación, almacenamiento y exposición de datos.
Domain Event suele referirse a una ocurrencia significativa dentro del modelo de dominio. Evans recomienda modelar aquello que interesa recordar a los expertos. DDD Reference, Domain Event. Integration Event suele denominar el hecho expuesto entre fronteras; los usos varían. Un evento interno no necesita publicarse externamente.
El contrato externo puede ser más grueso, sanitizado y versionado que el objeto interno. boundaries, granularity y modularity ayudan a evitar filtrar transiciones privadas. Eventos excesivamente finos pueden multiplicar volumen y dependencias; demasiado gruesos pueden difundir información innecesaria.
La evolución independiente requiere coexistencia de versiones interpretables. Cambios aditivos solo son compatibles si los consumidores los toleran; otra realización puede exigir contratos versionados y ventanas de migración. deployability necesita evidencia de esa coexistencia. Retención y múltiples consumidores hacen pertinente publicar solo datos justificados por sus contratos.
Commerce: política antes que flechas
Este ejemplo ilustrativo distingue aceptación de finalización. Orders recibe PlaceOrder, valida un invariante local y confirma pedido pendiente más intención de publicar OrderPlaced. Puede emplear transactional-outbox: las dos escrituras participan en una transacción local; el relay publica después. No existe por ello una transacción distribuida.
En una variante coreografiada:
Orders publica OrderPlaced
-> Inventory reserva o rechaza; publica su resultado
-> Payments reacciona según la política elegida
-> los resultados habilitan confirmar o rechazar el pedido
-> Shipping actúa cuando se acreditan las condicionesLa política está distribuida, sin un único controlador que dirija cada transición. Necesita correlacionar resultados, declarar quién confirma y decidir qué ocurre ante rechazo, vencimiento o pago tardío. Las flechas no resuelven esas obligaciones. Una política posible hace que Orders declare confirmado solo al acreditar reserva y autorización válidas; Shipping exige esa confirmación. Si Inventory rechaza, no se solicita pago; si Payments rechaza después de reservar, se pide liberar la reserva. Un resultado tardío debe confrontarse con el estado actual antes de aceptarse o compensarse. La responsabilidad está en contratos y participantes elegidos, no en el transporte. La autonomía local puede crecer mientras la lógica global resulta menos visible.
Una variante orquestada conserva estado del proceso explícitamente:
OrderWorkflow recibe OrderPlaced
-> solicita ReserveInventory
<- recibe InventoryReserved o InventoryRejected
-> solicita AuthorizePayment si corresponde
<- recibe PaymentAuthorized o PaymentRejected
-> decide confirmación, recuperación o compensaciónPuede reaccionar a eventos, emitir comandos y publicar nuevos hechos. EIP describe un Process Manager que mantiene el estado de la secuencia y decide próximos pasos. Sigue siendo compatible con organización event-driven.
coordination determina política, progreso y recuperación; saga-pattern ofrece contratos de pasos y compensación cuando corresponden. EDA no equivale a choreography, ni orchestration a request/response síncrono. Emitir OrderPlaced no prueba que el pedido esté confirmado, pagado o enviado.
Entrega, duplicados y orden
Hay que declarar supuestos de delivery-guarantees: qué significa aceptar, entregar y confirmar procesamiento. At-most-once puede admitir pérdida; at-least-once puede repetir. exactly-once-semantics obliga a delimitar qué efecto cubre una afirmación de exactly-once. Una garantía de transporte no asegura un único efecto de negocio en una API externa.
idempotency y idempotent-consumer permiten proteger efectos repetidos. inbox-pattern puede coordinar identidad procesada y cambio local; no define EDA. Outbox protege la relación entre cambio local e intención de publicar, sin garantizar todos los consumidores ni ser un requisito universal.
retry necesita considerar duplicados, orden y efectos externos. dead-letter-queue aparta mensajes que no progresan según su política: no compensa el negocio ni garantiza recuperación. Un responsable debe determinar si corregir, reintroducir o cerrar la intención.
Ordenar por entidad, partición o relación causal puede bastar; no se presupone un orden total global. Paralelismo y reentrega pueden alterar observación. Una versión o secuencia ayuda solo si el contrato define qué hacer ante un salto o dato atrasado. Exigir orden global puede aumentar coordinación y limitar scaling.
Decision Axes
architecture-decision-axes organiza estas preguntas, sin prometer resultados por la etiqueta:
| Eje | Tendencia o decisión que debe explicitarse |
|---|---|
| decomposition | Puede conectar módulos, servicios o sistemas; no prescribe fronteras. |
| dependency-structure | Contratos, semántica y canales siguen siendo dependencias. |
| communication | Eventos centrales; comandos y consultas pueden coexistir. |
| coordination | Choreography, orchestration y process managers son posibles. |
| data-ownership | Hechos desde autoridades; copias sin transferir ownership. |
| isolation | Buffering posible; infraestructura común conserva riesgos. |
| tenancy | Contexto, acceso, retención y recursos por tenant. |
| deployment | Contratos compatibles pueden permitir evolución separada. |
| execution-model | Workers, handlers y streams con lifecycles distintos. |
| scaling | Concurrencia y particiones, sujetas a orden, estado y capacidad. |
Un broker durable puede amortiguar una interrupción del consumidor; broker, topic, partición o cuota compartidos también pueden ser dominios comunes de fallo. No garantiza isolation. Tenant context necesita validación en consumidores; un tenant puede producir backlog o acceder a copias retenidas si faltan fronteras efectivas.
Los consumidores pueden ser workers continuos, handlers breves o procesadores de streams. Afinidad del estado, particiones calientes, dependencias externas y capacidad del intermediario condicionan scaling. Más consumidores no prometen más throughput indefinidamente.
Estado, carga y evidencia operacional
La propagación independiente suele producir ventanas de información atrasada. No convierte toda operación en eventualmente consistente: Orders puede proteger un invariante local transaccionalmente y publicar después. Exigir confirmación coordinada para ciertos resultados sigue siendo posible y tiene costos propios.
Cuando entrada supera procesamiento, backlog representa trabajo pendiente y demora creciente. backpressure y queue-management permiten examinar límites, edad, throughput y responsabilidad durable. Hohpe explica que invertir control mediante queues exige flow control. Queues invert control flow but require flow control. Retención y almacenamiento finitos no sustituyen capacidad sostenible.
Recuperar consumidores o ejecutar replay puede liberar trabajo acumulado sobre dependencias saturadas. load-and-flow-control, cascading-failures y retry-storms conectan esa sobrecarga diferida con políticas de admisión y repetición. Dejar de fallar inmediatamente upstream no elimina el riesgo.
operability exige reconstruir request, evento, consumidor y resultados posteriores. Una identidad de correlación agrupa la conversación; EIP, Correlation Identifier muestra la asociación entre request y reply. Registrar qué mensaje previo motivó otro aporta causación: una relación distinta de compartir grupo. Hohpe describe causality como relación entre eventos en Programming Without a Call Stack, página 2. No se impone un schema de identificadores.
La investigación necesita tiempos, estados y responsabilidad, además de IDs. La trazabilidad evita asumir que «publicado» significa «completado». Pruebas de contrato y escenarios de duplicación, desorden, interrupción y recuperación complementan pruebas unitarias; estas últimas no demuestran el recorrido distribuido completo.
Composición y trade-offs
serverless-architecture puede suministrar handlers bajo demanda y servicios gestionados para una EDA. EDA no requiere Serverless: puede operar sobre servicios de larga duración. Serverless tampoco exige EDA: una API HTTP gestionada puede no organizar interacción entre capacidades mediante hechos. Delegación de infraestructura y centralidad de eventos son preguntas distintas.
EDA puede coexistir con monolith: eventos locales no separan deployment. En modular-monolith, un dispatcher puede relacionar módulos dentro de un proceso, sin obligarlos a comunicar todo mediante eventos ni ofrecer durabilidad automática.
service-based-architecture y service-oriented-architecture pueden incorporar notificaciones entre capacidades. SOA no exige eventos ni EDA exige middleware o gobierno empresarial particulares. microservices puede emplear integración event-driven o principalmente requests directos. EDA no exige Microservices; ninguno representa una etapa superior del otro.
evolvability puede beneficiarse de consumidores nuevos con contratos estables; cambiar significado todavía afecta participantes. deployability puede mejorar con versiones compatibles y empeorar con schemas que requieren releases sincronizados. Autonomía, disponibilidad y respuesta rápida dependen de mecanismos y necesidades reales.
Los costos incluyen diagnóstico, pruebas, latencia hasta el resultado, copias, tráfico, almacenamiento y coordinación. La guía de Azure describe desafíos de entrega, consistencia, orden y seguimiento en una realización distribuida: sirven como evidencia de obligaciones, sin generalizar sus beneficios a toda EDA. Event-driven architecture style, Challenges.
testability y cost-efficiency requieren comparar escenarios, operación humana y recursos completos. essential-complexity y accidental-complexity ayudan a preguntar si fan-out, autonomía o buffering responden a drivers, o si agregamos mecanismos sin necesidad demostrada. EDA en sí misma no es accidental.
Antes de elegir, declarar hechos, autoridades, completion policy, contratos, supuestos de entrega y recuperación. Contar eventos o instalar un broker no demuestra calidad arquitectónica. architecture-profiles ofrece navegación y comparación situada; quality-attribute-trade-offs mantiene el razonamiento sobre consecuencias abiertas.