Arquitectura Event-Driven App Web Medida
Cómo diseñar una arquitectura event-driven para tu aplicación web a medida
Llega un punto en el que tu aplicación deja de ser manejable. Tocas el módulo de pedidos y se rompe el de facturación. Cada despliegue se vuelve una negociación entre equipos. Y cuando quieres escalar una parte concreta, descubres que está atada a media docena de servicios.
Aquí es donde entender cómo diseñar una arquitectura event-driven para tu aplicación web a medida deja de ser un capricho de ingeniería y se convierte en una decisión de negocio. Esta guía recorre los patrones, las tecnologías que merece la pena evaluar y los puntos donde la mayoría de equipos se tropieza la primera vez.
Qué es realmente una arquitectura event-driven
En lugar de que el servicio A llame al servicio B para pedirle algo, A publica un hecho —"PedidoCreado", "PagoConfirmado", "UsuarioRegistrado"— y deja que quien esté interesado reaccione. Sencillo en el enunciado, denso en sus implicaciones.
Lo importante: un evento no es una orden. Es una declaración inmutable de algo que ya ocurrió. Eso cambia por completo cómo razonas sobre el sistema. Ya no piensas en "qué tiene que hacer B cuando A se lo pide", sino en "qué cosas pasan en mi dominio y quién debería enterarse".
Cómo difiere del clásico request-response
En el modelo tradicional, A conoce a B, lo llama por HTTP o gRPC, y espera respuesta. Si B está caído, A se cae con él. Si mañana C también necesita reaccionar a lo mismo, alguien tiene que volver a tocar A para añadir esa llamada. Y todo el mundo tiene que estar despierto a la vez.
Con eventos, A publica y se olvida. B procesa cuando puede. Si una semana después aparece C, se suscribe y listo: nadie tocó al productor. Ese desacoplamiento temporal —no necesitar que todo esté vivo al mismo tiempo— es probablemente el beneficio más infravalorado de EDA.
Patrones que conviene tener claros
Event sourcing
En vez de guardar solo el estado actual de una entidad, guardas la secuencia completa de eventos que llevaron a él. El estado lo reconstruyes reproduciendo el log.
Ventajas: auditoría perfecta, capacidad de viajar al pasado, posibilidad de corregir un bug y volver a aplicar los eventos con la lógica nueva. Cuándo usarlo: banca, fintech, cualquier dominio donde alguien va a preguntarte por qué una entidad acabó en cierto estado. El precio: snapshots para no reconstruir desde el evento 1 cada vez, esquemas que evolucionan a lo largo de los años, y queries complejas que te obligan a mantener proyecciones materializadas.
CQRS
Separas el modelo de escritura del modelo de lectura. Los comandos generan eventos que actualizan vistas optimizadas para consultas. Tus escrituras pueden vivir en PostgreSQL y tus lecturas en Elasticsearch o en una caché especializada, sin que un dato se contamine al otro.
Funciona especialmente bien cuando los patrones de lectura y escritura son muy distintos —piensa en un dashboard analítico construido sobre datos transaccionales—. ¿El coste? Consistencia eventual entre modelos, duplicación deliberada de datos y la disciplina de mantener proyecciones sincronizadas.
Pub/Sub
El patrón más básico: un publicador emite a un topic, varios suscriptores consumen de forma independiente. Es el punto de partida natural para notificaciones, propagación de cambios entre sistemas internos o integraciones con terceros. Si nunca has hecho EDA, empieza aquí antes de meterte en event sourcing.
Event streaming
A diferencia del pub/sub clásico, donde el mensaje se consume y desaparece, el streaming mantiene un log persistente que puedes releer. Kafka es el ejemplo canónico. Esto desbloquea cosas potentes: rebobinar el flujo para depurar, dar de alta un servicio nuevo y dejar que se "ponga al día" con seis meses de historia, o procesar el mismo evento desde dos pipelines distintos a velocidades distintas.
Tecnologías que merece la pena evaluar
Apache Kafka
Si necesitas millones de eventos por segundo, retención larga y múltiples grupos de consumidores leyendo cada uno a su ritmo, Kafka es el estándar. Su modelo de particiones también te garantiza orden dentro de cada una, lo cual es oro cuando trabajas con flujos donde la secuencia importa (pagos, cambios de estado de un pedido).
Operativamente es exigente. Mantener un clúster sano —rebalanceos, replicación, retención por topic, monitorización— requiere gente que sepa. Si no tienes ese músculo, los servicios gestionados como Confluent Cloud, AWS MSK o Aiven te quitan el 80% del dolor a cambio de factura mensual.
RabbitMQ
Más maduro, más versátil para patrones de mensajería variados (pub/sub, routing por reglas, RPC), y mucho menos pesado de operar que Kafka. Si manejas miles o cientos de miles de mensajes por segundo y necesitas routing inteligente, RabbitMQ probablemente sea mejor opción que forzar Kafka donde no toca.
Redis Streams
Un log de eventos ligero embebido en Redis. Encaja cuando ya tienes Redis desplegado, tus volúmenes son medios y la latencia ultra-baja pesa más que la durabilidad absoluta. No lo elegiría para procesar transacciones bancarias; sí para una cola de jobs con tracking de progreso o para distribuir eventos efímeros entre instancias de un mismo servicio.
Cuándo EDA tiene sentido y cuándo es disparar a una mosca con un cañón
Una regla práctica que suelo dar a clientes: si puedes resolver el problema con una llamada síncrona directa sin generar acoplamiento problemático, hazlo. EDA introduce complejidad inherente —consistencia eventual, debugging distribuido, gestión de errores asíncronos— que solo se paga sola cuando los beneficios son evidentes.
EDA encaja bien cuando varios servicios necesitan reaccionar al mismo hecho de negocio, cuando los componentes deben escalar de forma independiente, cuando la auditoría es un requisito real, o cuando ves venir un crecimiento serio en complejidad y volumen.
EDA es over-engineering cuando tu aplicación es un monolito sencillo con cuatro módulos, cuando el equipo no ha trabajado nunca con sistemas distribuidos, cuando necesitas consistencia inmediata en todos los flujos, o cuando no tienes presupuesto para la infraestructura adicional. He visto startups montar Kafka para gestionar diez pedidos al día y morir de éxito operativo antes de tener un cliente.
Diseñar eventos que no te exploten en la cara
Un evento bien diseñado lleva ID único (para deduplicación), tipo descriptivo en pasado ("PedidoCreado", no "Pedido" ni "CrearPedido"), timestamp del hecho, origen del servicio que lo emite, versión del esquema, payload con los datos relevantes y metadata para trazabilidad: correlation ID, trace ID, user ID si aplica.
Versionado de esquemas: el día que añadas un campo
Los eventos viven años. Mañana querrás añadir un campo, pasado renombrar uno, y dentro de seis meses cambiar un formato. Necesitas dos compatibilidades claras: hacia atrás, donde un consumidor antiguo sigue leyendo eventos nuevos (logrado añadiendo campos opcionales y no borrando nunca), y hacia adelante, donde consumidores nuevos saben tolerar eventos viejos a los que les faltan campos. Schema Registry de Confluent automatiza la validación de esa compatibilidad cada vez que despliegas; merece la pena el esfuerzo de adoptarlo desde el principio.
Idempotencia, no negociable
En sistemas distribuidos un evento puede llegar dos veces. Es una propiedad del modelo, no un bug. Tus consumidores tienen que estar diseñados para que procesar el mismo evento dos veces no genere efectos duplicados. Las tres estrategias que funcionan: guardar IDs procesados y comprobar antes de actuar, diseñar las operaciones como upserts en vez de inserts, o usar claves naturales de negocio (número de factura, referencia externa) para detectar repeticiones.
Errores y dead letter queues
No todos los eventos se procesan a la primera. Tu estrategia mínima debería incluir reintentos con backoff exponencial —tres a cinco intentos con esperas crecientes tipo 1s, 5s, 30s, 2min—, una dead letter queue donde acaben los eventos que agotaron reintentos, alertas en cuanto algo cae a la DLQ (si esperas a la queja del cliente, ya llegas tarde) y un circuit breaker que pause temporalmente un consumidor que está fallando en bucle para no saturar al broker ni a los servicios downstream.
Observabilidad: el talón de Aquiles
Depurar un flujo event-driven cuesta más que depurar una cadena de llamadas síncronas. No hay un stack trace que te lo cuente todo. La única forma de mantener la cordura es el distributed tracing con correlation IDs que se propagan de un evento al siguiente, de modo que puedas reconstruir la cascada completa en Jaeger, Zipkin, AWS X-Ray o Google Cloud Trace.
Las métricas que tienes que mirar a diario: lag de consumidores (cuántos eventos esperan a ser procesados —si crece, tu consumidor no aguanta el ritmo—), throughput por topic, latencia end-to-end desde publicación hasta consumo, profundidad de las DLQ y edad del mensaje más antiguo sin procesar. Esa última métrica es la que mejor delata problemas silenciosos.
Migrar desde un monolito sin romperlo todo
Pasar de monolito a EDA de un día para otro es la mejor receta para un fracaso caro. El camino sensato es por fases.
Primero, strangler pattern: eliges un flujo concreto que se beneficie del desacoplamiento e introduces eventos solo ahí. El resto sigue funcionando como antes. Después, dual write temporal: el monolito escribe en su base de datos legacy y a la vez publica eventos al bus; los nuevos servicios consumen del bus. Cuando todo funciona estable durante semanas, dejas de escribir en la BD legacy. Y por último, inversión del control: progresivamente más lógica migra a servicios que reaccionan a eventos, hasta que el antiguo monolito acaba siendo un servicio más dentro del ecosistema.
Lo que ganas en una aplicación web a medida
Escalas solo lo que lo necesita. Si el procesamiento de imágenes se satura un viernes por la tarde, añades réplicas de ese consumidor y nada más. Despliegas cada servicio sin coordinar con el resto, lo que baja el riesgo y aumenta la frecuencia con la que puedes entregar valor. Y cuando algo cae, los eventos se acumulan en el broker en lugar de perderse: el sistema absorbe el fallo y se recupera cuando el servicio vuelve. Añadir funcionalidad nueva muchas veces consiste en crear un consumidor que se suscribe a eventos que ya existen, sin tocar una línea del código viejo.
Conclusión
Saber cómo diseñar una arquitectura event-driven para tu aplicación web a medida no consiste en aplicar Kafka a todo lo que se mueve. Consiste en identificar qué partes de tu sistema realmente necesitan desacoplamiento, elegir el patrón mínimo que resuelve el problema y comprometerte con la disciplina operativa que viene en el lote: idempotencia, versionado, observabilidad y manejo decente de errores.
Si estás valorando si EDA es el camino adecuado para tu proyecto o quieres diseñar desde cero la arquitectura de una aplicación que tiene que crecer sin romperse, hablemos y te ayudamos a tomar la decisión técnica correcta para tu caso concreto.