Arquitectura orientada a eventos sin el caos
La arquitectura orientada a eventos promete acoplamiento débil y escala, pero introduce en silencio nuevos modos de fallo. Así se obtienen los beneficios sin los incidentes de las 3 de la madrugada.
Por Innovation T Team
La arquitectura orientada a eventos se vende como la cura para el acoplamiento fuerte, y puede serlo. También puede convertir una base de código limpia en una novela de misterio distribuida en la que nadie logra explicar por qué se cobró un pedido dos veces. La diferencia no está en el broker que elijas. Está en el puñado de decisiones que tomas sobre los eventos, las garantías de entrega y la observabilidad antes de que fluya el primer mensaje.
Qué te aporta realmente el enfoque orientado a eventos
En esencia, la arquitectura orientada a eventos (EDA) significa que los servicios se comunican publicando hechos sobre cosas que ocurrieron, en lugar de llamarse directamente entre sí y esperar. Un servicio de pagos emite PaymentCaptured. Quien tenga interés (preparación de pedidos, analítica, notificaciones) reacciona a su propio ritmo. Nadie bloquea a nadie.
Las verdaderas ganancias aparecen en tres lugares:
- Desacoplamiento en el tiempo. Un consumidor puede estar caído durante diez minutos y ponerse al día después. En una cadena sincrónica, esa misma caída se propaga en cascada aguas arriba.
- Desacoplamiento en la propiedad de los equipos. Añadir un consumidor nuevo no requiere tocar al productor. Las nuevas funcionalidades pasan a ser "suscribirse y reaccionar" en lugar de "modificar y volver a desplegar el núcleo".
- Difusión natural en abanico. Un solo evento puede desencadenar cinco reacciones sin que el productor sepa que ninguna de ellas existe.
Según nuestra experiencia, los equipos recurren a EDA en el momento exacto en que su grafo de llamadas sincrónicas empieza a parecer un plato de espaguetis. Ese instinto suele ser acertado. El error es suponer que la capa de mensajería elimina la complejidad. La reubica. Cambias la complejidad visible de las llamadas a funciones por la complejidad invisible de la semántica de entrega.
Eventos frente a comandos: la distinción que evita la mitad de tus errores
Este es el modelo mental más útil, y la mayoría de los sistemas caóticos lo hacen mal.
- Un evento es la constatación de un hecho pasado.
InvoiceIssued. Ya ha ocurrido. Se difunde. Al productor no le importa quién escucha y no espera respuesta. Cero, uno o diez consumidores, todo está bien. - Un comando es una solicitud de que algo ocurra en el futuro.
IssueInvoice. Va dirigido a exactamente un manejador y puede ser rechazado.
Cuando difuminas esta frontera, obtienes productores que dependen en secreto de que un consumidor concreto esté en ejecución, lo cual es acoplamiento disfrazado de evento. Una buena regla: nombra los eventos en tiempo pasado y nunca codifiques en ellos la expectativa de una respuesta. Si un servicio publica OrderPlaced y luego sondea buscando InvoiceIssued antes de poder continuar, no has construido EDA. Has construido una llamada a procedimiento remoto lenta y difícil de depurar.
Granularidad de los eventos y forma del payload
Otras dos decisiones muerden a los equipos más tarde:
- Eventos delgados frente a gordos. Un evento delgado lleva un identificador y espera que los consumidores obtengan los detalles. Un evento gordo lleva el estado completo. Los eventos delgados mantienen los payloads pequeños pero provocan una estampida de llamadas de vuelta al productor, reintroduciendo acoplamiento. Los eventos gordos son autosuficientes pero pueden filtrar el esquema interno. Solemos preferir eventos moderadamente gordos que lleven los datos que un consumidor razonable necesita, más un campo de versión.
- Versionado del esquema desde el primer día. Añade una
versiony trata el payload como un contrato público. Los cambios aditivos (nuevos campos opcionales) son seguros. Eliminar o renombrar campos es un cambio disruptivo que necesita una versión nueva y una ventana de migración.
La garantía de entrega cuya letra pequeña nadie lee
Casi todos los brokers modernos (Kafka, RabbitMQ, AWS SNS/SQS, Google Pub/Sub, NATS) te dan por defecto una entrega at-least-once. Léelo de nuevo. At-least-once significa que los duplicados no son un caso extremo. Son un martes cualquiera. Cortes de red, reinicios de consumidores y rebalanceos provocan todos que el mismo mensaje llegue dos veces.
La consecuencia práctica: cada consumidor debe ser idempotente. Procesar PaymentCaptured dos veces no debe cobrar al cliente dos veces ni enviar dos recibos. Esto no es un endurecimiento opcional que añades después. Es el muro de carga.
Maneras en que implementamos la idempotencia en sistemas reales:
- Dale a cada evento un identificador estable y único en el productor.
- En el consumidor, registra los identificadores procesados en una tabla de deduplicación (o una caché indexada con un TTL razonable) dentro de la misma transacción que hace el trabajo.
- Antes de actuar, comprueba si el identificador ya fue tratado. Si es así, confirma la recepción y sigue adelante.
- Para las actualizaciones de estado, prefiere operaciones naturalmente idempotentes (fijar el saldo a X, no sumar X al saldo).
La entrega exactly-once es en su mayor parte una frase de marketing. Lo que sí puedes lograr es entrega at-least-once más procesamiento idempotente, que desde fuera equivale a effectively-once. Diseña para eso y dejarás de perseguir fantasmas.
Ordenamiento y la trampa de la escritura doble
Dos modos de fallo sutiles causan una parte desproporcionada de los incidentes en producción.
Ordenamiento. La mayoría de los brokers solo garantizan el orden dentro de una partición o una única cola, no de forma global. Si AccountCreated y AccountDeleted caen en particiones distintas, un consumidor puede ver primero el borrado. La solución es particionar por una clave estable (normalmente el identificador del agregado) para que todos los eventos de una entidad mantengan su orden. Acepta que obtienes ordenamiento por entidad, no global, y diseña consumidores que toleren el reordenamiento entre entidades.
El problema de la escritura doble. Tu servicio actualiza su base de datos y luego publica un evento. Si el proceso se cae entre esos dos pasos, has confirmado el estado pero perdido el evento, o has publicado el evento de un cambio de estado que se revirtió. No puedes hacer que dos sistemas separados confirmen de forma atómica con un try/catch ingenuo.
La solución estándar es el patrón outbox transaccional:
- En una única transacción de base de datos local, escribe tu cambio de negocio e inserta el evento en una tabla
outbox. - Un proceso relé separado (o una herramienta de change-data-capture que lee el registro de transacciones) lee el outbox y publica al broker.
- Marca las filas como publicadas, reintentando con seguridad porque los consumidores son idempotentes de todos modos.
Esto convierte una escritura doble poco fiable en una única confirmación local fiable más una publicación con consistencia eventual. Es aburrido, y lo aburrido es exactamente lo que quieres en tus rutas que mueven dinero. Los equipos que pasan de una sola base de datos hacia servicios chocan con este muro constantemente, y por eso cubrimos la transición más amplia en nuestra guía sobre pasar del monolito a los microservicios.
Observabilidad, o volarás a ciegas
Los sistemas sincrónicos fallan a gritos con una traza de pila. Los sistemas orientados a eventos fallan en silencio. Un mensaje se esfuma, un consumidor se queda atrás sin ruido, y te enteras por un cliente enfadado. No puedes lanzar EDA sin una observabilidad diseñada desde el principio.
Los innegociables:
- Identificadores de correlación y causalidad en cada evento, propagados de extremo a extremo, para poder reconstruir un flujo de negocio completo a través de los servicios.
- Trazado distribuido (OpenTelemetry es el estándar de facto en 2026) para que una sola traza abarque al productor, al broker y a cada consumidor.
- Métricas de retraso del consumidor (lag). Alerta sobre el retraso, no solo sobre los errores. Un consumidor que está activo pero con 40 000 mensajes de retraso es una caída que ningún health check detectará.
- Colas de mensajes fallidos (DLQ). Los mensajes que fallan repetidamente deben aterrizar en un lugar visible, con herramientas para inspeccionarlos, corregirlos y reproducirlos. Una DLQ que nadie vigila no es más que una forma más lenta de perder datos.
Una buena higiene de esquema se relaciona con esto. Si tus eventos son contratos limpios y versionados, el trazado y la reproducción resultan mucho más fáciles, que es la misma disciplina que describimos en diseñar APIs que los desarrolladores adoran.
Cuándo NO ir hacia lo orientado a eventos
La ingeniería sénior consiste en saber cuándo decir que no. EDA es la opción por defecto equivocada cuando:
- La interacción es genuinamente de petición/respuesta y quien llama necesita una respuesta ahora (una verificación de inicio de sesión, una cotización de precio al pagar). Forzar eso en eventos añade latencia y complejidad para nada.
- Tu equipo nunca ha operado un broker en producción y la fecha límite es el mes que viene. La curva de aprendizaje operativa es real.
- Tienes tres servicios y ningún dolor de escala. Puede que estés comprando problemas de sistemas distribuidos para resolver un problema de acoplamiento que todavía no tienes.
Un camino intermedio pragmático que recomendamos a menudo: conserva las llamadas sincrónicas para las lecturas de cara al usuario que necesitan una respuesta inmediata, y usa eventos para los efectos secundarios (notificaciones, analítica, aprovisionamiento posterior) que pueden ocurrir justo después. Lo híbrido no es un fracaso. Suele ser la arquitectura correcta.
Una lista de comprobación previa al lanzamiento
Antes de que tu primer evento llegue a producción, confirma cada uno de estos puntos:
- Cada evento tiene un identificador único y una versión de esquema.
- Cada consumidor es idempotente y está probado frente a la entrega duplicada.
- Los productores usan el patrón outbox (o CDC) en lugar de escrituras dobles.
- Las claves de partición garantizan el ordenamiento para cada entidad que lo necesita.
- Los identificadores de correlación fluyen a través de cada salto y aparecen en los logs y las trazas.
- Las DLQ existen, están monitorizadas y tienen un procedimiento de reproducción documentado.
- El retraso del consumidor tiene un umbral de alerta, no solo una alerta de errores.
- Los cambios de esquema tienen una política de versionado y de obsolescencia por escrito.
Si no puedes marcar los ocho, no estás listo para escalar el patrón. Estás listo para prototiparlo.
Cómo puede ayudarte Innovation T
La arquitectura orientada a eventos recompensa a los equipos que hacen bien las partes poco glamorosas: la idempotencia, el outbox, el ordenamiento y la observabilidad. Ahí es precisamente donde invertimos nuestro tiempo. En Innovation T, nuestros equipos de ingeniería de software y de nube diseñan sistemas orientados a eventos construidos para ser depurados, no solo para lucir en una demo. Te ayudamos a elegir el broker adecuado para tu carga de trabajo, a implementar outbox transaccionales, a cablear el trazado con OpenTelemetry y a configurar la reproducción de DLQ para que un despliegue defectuoso sea una recuperación de cinco minutos en lugar de un fin de semana perdido.
Si estás sopesando un salto a la mensajería, la descomposición de un monolito o el intento de domar un sistema que ya se ha vuelto caótico, podemos ayudarte a hacerlo de forma deliberada. Explora nuestros servicios o ponte en contacto y déjanos trazar el camino más limpio para tu arquitectura.
¿Listo para construir con Innovation T?
Ya se trate de seguridad, crecimiento o ingeniería, nuestro equipo puede ayudarte a lograrlo con calidad.