Saltar al contenido

Event-driven architecture para operaciones logísticas

A
abemon
| | 12 min de lectura | Escrito por profesionales
Compartir

Por qué la logística es inherentemente event-driven

Un envío no es un registro en una base de datos. Es una secuencia de eventos: recogido, en tránsito, en aduana, liberado, en reparto, entregado. O, más frecuentemente: recogido, en tránsito, retenido en aduana, documentación incompleta, retenido en aduana (segunda notificación), documentación aportada, liberado, en reparto, destinatario ausente, segundo intento, entregado.

La realidad operativa de la logística es que las excepciones no son excepciones. Son el camino normal. En un operador logístico típico, entre el 15% y el 30% de los envíos experimentan al menos una incidencia: retrasos en aduana, direcciones incorrectas, mercancías dañadas, vehículos averiados, cierres portuarios. Cada una de estas incidencias genera una cascada de acciones: notificar al cliente, recalcular plazos, reasignar rutas, generar documentación alternativa.

La arquitectura clásica request-response no está diseñada para esto. Un API REST que dice “dame el estado del envío” devuelve un snapshot estático. No te dice qué acaba de cambiar, ni activa automáticamente las acciones necesarias. Para eso necesitas una arquitectura donde los eventos fluyen y los sistemas reaccionan.

Anatomía de un evento logístico

Un evento logístico bien diseñado tiene una estructura que captura no solo qué pasó, sino el contexto necesario para que cualquier sistema downstream pueda actuar sin consultar fuentes adicionales:

{
  "eventId": "evt_7f3a8b2c",
  "eventType": "shipment.customs.held",
  "timestamp": "2025-05-22T14:32:00Z",
  "source": "customs-gateway",
  "shipmentId": "SHP-2025-0847",
  "data": {
    "customsOffice": "ES002801",
    "holdReason": "DOCUMENTATION_INCOMPLETE",
    "missingDocuments": ["T1", "DUA"],
    "estimatedClearanceDelay": "PT48H",
    "currentLocation": {
      "port": "ESALG",
      "warehouse": "ALGECIRAS-ZAL-3"
    }
  },
  "metadata": {
    "clientId": "CLT-1234",
    "serviceLevel": "express",
    "originalETA": "2025-05-24T09:00:00Z",
    "correlationId": "flow_abc123"
  }
}

Varios principios en este diseño:

Eventos inmutables y auto-contenidos. El evento contiene toda la información necesaria para procesarse. Un consumidor no debería necesitar llamar al API de envíos para saber qué documentos faltan. Si el evento lo incluye, el procesamiento es local, rápido y resiliente a fallos de otros servicios.

Tipado estricto. shipment.customs.held es específico. No shipment.updated con un campo genérico status. Los consumidores se suscriben a los eventos que les importan, no a todos los eventos y luego filtran.

Correlation ID. Conecta todos los eventos de un mismo flujo de negocio. Cuando necesitas reconstruir la historia completa de un envío, el correlation ID es el hilo conductor.

Metadata de negocio. serviceLevel: "express" no es un detalle técnico; es información que determina la prioridad de respuesta ante la excepción. Un envío express retenido en aduana activa un flujo diferente (más urgente, más caro) que uno estándar.

Topología de eventos para logística

La organización de los eventos en topics (Kafka) o canales sigue la estructura del dominio de negocio:

shipments.lifecycle    -> created, picked-up, in-transit, delivered
shipments.customs      -> submitted, held, cleared, rejected
shipments.exceptions   -> delay, damage, address-invalid, lost
shipments.financial    -> invoiced, payment-received, disputed
vehicles.tracking      -> position-update, eta-recalculated
warehouse.inventory    -> received, stored, picked, dispatched

Cada topic agrupa eventos del mismo dominio. Los consumidores se suscriben al topic relevante. Un sistema de notificaciones al cliente se suscribe a shipments.lifecycle y shipments.exceptions. El sistema financiero se suscribe a shipments.financial. El TMS se suscribe a vehicles.tracking.

La tentación es crear un único topic shipments con todo. Funciona cuando tienes 3 consumidores. Deja de funcionar cuando tienes 15 y cada uno procesa el 10% de los eventos, descartando el 90% restante. Separar por dominio reduce la carga de procesamiento y simplifica el ownership de los topics.

Gestión de excepciones automatizada

Aquí es donde la arquitectura event-driven brilla en logística. Las excepciones no son solo eventos pasivos que se registran; son triggers que activan flujos de compensación automáticos.

Patrón: Saga para incidencias de aduana

Cuando un envío queda retenido en aduana, se activa una saga (secuencia de pasos con compensación):

  1. Evento: shipment.customs.held con razón DOCUMENTATION_INCOMPLETE.
  2. Acción: El servicio de documentación verifica que documentos tiene y cuáles faltan. Si los tiene en el sistema pero no se enviaron, los reenvía automáticamente.
  3. Acción: El servicio de notificaciones avisa al cliente con los documentos faltantes y un plazo para aportarlos.
  4. Acción: El servicio de planificación recalcula la ETA basándose en el retraso estimado y actualiza todos los envíos encadenados.
  5. Timeout: Si en 24 horas el cliente no aporta los documentos, escalar a un agente humano.
  6. Compensación: Si la aduana rechaza definitivamente, activar flujo de devolución o almacenamiento en depósito temporal.

Cada paso es un evento que dispara el siguiente. Si un paso falla, la saga tiene una ruta de compensación definida. Sin esta orquestación, cada incidencia aduanera requiere intervención manual: alguien mira el correo, llama al cliente, envía los documentos, actualiza el sistema. Multiplicado por 50 incidencias diarias, eso son 2-3 personas a jornada completa solo para gestión aduanera.

Re-enrutamiento automático

El re-enrutamiento es un caso de uso donde la latencia importa:

Evento: vehicle.breakdown (vehiculo averiado)
  -> Route service: recalcular ruta para envios afectados
  -> Fleet service: asignar vehiculo alternativo mas cercano
  -> Notification service: avisar a destinatarios de cambio de ETA
  -> Financial service: calcular coste adicional
Todo en < 5 minutos desde el evento inicial.

En un sistema request-response, este flujo requiere que un operador humano detecte el problema, consulte múltiples sistemas, tome decisiones y actualice manualmente. Con eventos, el flujo se ejecuta automáticamente. El operador humano revisa y valida, pero no ejecuta.

Integración con TMS y WMS legacy

El elefante en la habitación. La mayoría de las empresas logísticas operan con un TMS (Transportation Management System) y/o un WMS (Warehouse Management System) que tiene 10-20 años, no expone eventos, y funciona con procesos batch nocturnos.

Hay que convivir con ellos. Reemplazarlos es un proyecto de millones y años. La estrategia: Change Data Capture (CDC) con Debezium para convertir cambios en la base de datos del sistema legacy en eventos.

CDC con Debezium

Debezium conecta con la base de datos del TMS/WMS (MySQL, PostgreSQL, Oracle, SQL Server), monitoriza el log de transacciones, y publica cada cambio como un evento en Kafka. No requiere modificar el sistema legacy.

TMS (Oracle) -> Debezium -> Kafka topic: tms.shipments.changes
  -> Transformer: mapear estructura legacy a eventos de dominio
  -> Kafka topic: shipments.lifecycle

El transformer es clave. Los datos del TMS legacy no tendrán la estructura de tus eventos de dominio. Un campo STATUS_CD = 'DLV' en la tabla SHIPMENTS necesita mapearse a un evento shipment.delivered con la estructura correcta. Este mapping es específico para cada sistema y es donde se concentra el trabajo de integración.

Problemas reales con CDC en logística:

Eventos duplicados: Debezium garantiza at-least-once delivery. Tu consumidor necesita ser idempotente. Si un evento shipment.delivered llega dos veces, no debe generar dos facturas.

Datos incompletos: El TMS legacy almacena la información de forma diferente a como la necesitas. El evento puede necesitar enriquecer datos de otra fuente (master data de clientes, tarifas) antes de ser útil. Esto se resuelve con un servicio de enriquecimiento que consulta las fuentes adicionales y completa el evento.

Latencia vs consistencia: CDC desde el log de transacciones tiene latencia de segundos. Si necesitas consistencia inmediata (la factura se genera en el momento exacto de la entrega), necesitas una señalización directa desde el proceso operativo, no desde CDC.

APIs como puerta intermedia

Para sistemas legacy que no permiten acceso al log de transacciones (algunos TMS propietarios), la alternativa es construir una capa API sobre el sistema legacy:

  1. Un API wrapper que exponga los datos del TMS vía REST.
  2. Un polling service que consulta el API cada N segundos buscando cambios.
  3. El polling service publica los cambios detectados como eventos en Kafka.

Es menos elegante que CDC y tiene mayor latencia (depende del intervalo de polling). Pero funciona cuando CDC no es viable. Lo crítico es que el polling service sea resiliente: mantenga un cursor de último cambio procesado y se recupere de fallos sin perder ni duplicar eventos.

Patrones de procesamiento

Event Sourcing para trazabilidad

En logística, la trazabilidad no es un nice-to-have; es un requisito legal (especialmente para mercancías peligrosas, productos alimentarios, y comercio internacional). Event sourcing almacena el estado del envío como una secuencia de eventos en vez de como un registro mutable:

SHP-2025-0847:
  [1] shipment.created      (2025-05-20T10:00:00Z)
  [2] shipment.picked-up    (2025-05-20T14:30:00Z)
  [3] shipment.in-transit    (2025-05-20T15:00:00Z)
  [4] shipment.customs.held  (2025-05-22T14:32:00Z)
  [5] shipment.customs.cleared (2025-05-23T09:15:00Z)
  [6] shipment.delivered     (2025-05-24T11:20:00Z)

El estado actual se deriva reproduciendo los eventos. Cualquier auditor (o cliente) puede ver la historia completa del envío. Si alguien pregunta “por qué se retrasó este envío?”, la respuesta está en la secuencia de eventos, no en un campo delay_reason que alguien rellenase manualmente (o no).

CQRS para consultas operativas

El patrón CQRS (Command Query Responsibility Segregation) separa las escrituras (eventos) de las lecturas (vistas materializadas). Es especialmente útil en logística porque los patrones de lectura y escritura son muy diferentes:

  • Escritura: Un evento cada vez que algo pasa. Alta frecuencia, baja complejidad.
  • Lectura: Un operador quiere ver todos los envíos en tránsito, filtrados por ruta, ordenados por ETA, con alertas de retraso. Alta complejidad, baja tolerancia a latencia.

La vista materializada (una tabla optimizada para lectura, actualizada asíncronamente por los eventos) sirve las consultas operativas sin impactar el rendimiento del pipeline de eventos.

Elección de broker

Apache Kafka es el estándar de facto para event streaming en logística. Razones: throughput alto (millones de eventos/segundo), retención configurable (los eventos se almacenan, no desaparecen tras consumirlos), garantías de orden dentro de una partición, y un ecosistema maduro (Kafka Connect, Kafka Streams, ksqlDB).

Amazon Kinesis / Google Pub/Sub / Azure Event Hubs: Alternativas cloud-native que reducen la operativa. Menos flexibles que Kafka pero más fáciles de operar si ya estás en un cloud provider. Kinesis Data Streams con el esquema de particiones es conceptualmente similar a Kafka.

RabbitMQ: Mejor como message broker (un consumidor procesa cada mensaje) que como event broker (múltiples consumidores procesan el mismo evento). Si tu caso es más “enviar tareas a workers” que “publicar eventos para quien quiera escuchar”, RabbitMQ es más sencillo.

Para operaciones logísticas con múltiples sistemas consumiendo los mismos eventos, Kafka es la elección correcta. La inversión en operativa se justifica con la flexibilidad y el rendimiento.

Métricas y observabilidad

Una arquitectura event-driven sin observabilidad es una caja negra. Las métricas críticas:

  • Consumer lag: Cuántos eventos hay sin procesar por cada consumidor. Si el lag crece, el consumidor no procesa al ritmo de producción. Es la métrica más importante.
  • End-to-end latency: Tiempo desde que el evento se produce hasta que el último consumidor lo procesa. Para notificaciones al cliente, debería ser < 30 segundos.
  • Error rate por topic: Eventos que fallan en procesamiento. Una tasa de error > 1% indica un problema sistemático.
  • Dead letter queue size: Eventos que fallaron repetidamente y fueron enviados a la cola de errores. Cada evento en la DLQ es una incidencia operativa no resuelta.

Herramientas: Kafka UI (o Confluent Control Center) para monitorizar el cluster. Prometheus + Grafana para métricas de aplicación. Alertas basadas en consumer lag y DLQ size son las dos que merecen despertar a alguien a las 3 de la mañana.

Empezar pequeño

No necesitas migrar toda tu operativa a event-driven de golpe. El patrón que funciona:

  1. Elegir un flujo: Notificaciones de tracking al cliente. Es visible, tiene valor inmediato, y no requiere modificar los sistemas core.
  2. Implementar CDC sobre el TMS existente para capturar cambios de estado de envío.
  3. Publicar eventos en Kafka.
  4. Construir el servicio de notificaciones como consumidor de eventos.
  5. Medir: Latencia de notificación, tasa de entrega, reducción de llamadas al call center.
  6. Expandir: Añadir flujos de excepciones automatizadas, re-enrutamiento, facturación.

El valor del primer flujo es demostrar que la arquitectura funciona y cuantificar el impacto. Con datos reales, es mucho más fácil justificar la inversión para expandir a los flujos siguientes.

Si te interesa el tracking de envíos en tiempo real como caso de uso concreto, detallamos la arquitectura en nuestro artículo sobre tracking de envíos en tiempo real. La logística no necesita ser time-consuming y manual. Los eventos están ocurriendo; la pregunta es si tus sistemas los escuchan o los ignoran.

Sobre el autor

A

abemon engineering

Equipo de ingenieria

Equipo multidisciplinar de ingenieria, datos e IA con sede en Canarias. Construimos, desplegamos y operamos soluciones de software a medida para empresas de cualquier escala.

Síguenos: LinkedIn GitHub