Saltar al contenido

Event-driven architecture para operaciones logisticas

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

Por que la logistica es inherentemente event-driven

Un envio no es un registro en una base de datos. Es una secuencia de eventos: recogido, en transito, en aduana, liberado, en reparto, entregado. O, mas frecuentemente: recogido, en transito, retenido en aduana, documentacion incompleta, retenido en aduana (segunda notificacion), documentacion aportada, liberado, en reparto, destinatario ausente, segundo intento, entregado.

La realidad operativa de la logistica es que las excepciones no son excepciones. Son el camino normal. En un operador logistico tipico, entre el 15% y el 30% de los envios experimentan al menos una incidencia: retrasos en aduana, direcciones incorrectas, mercancias danadas, vehiculos averiados, cierres portuarios. Cada una de estas incidencias genera una cascada de acciones: notificar al cliente, recalcular plazos, reasignar rutas, generar documentacion alternativa.

La arquitectura clasica request-response no esta disenada para esto. Un API REST que dice “dame el estado del envio” devuelve un snapshot estatico. No te dice que acaba de cambiar, ni activa automaticamente las acciones necesarias. Para eso necesitas una arquitectura donde los eventos fluyen y los sistemas reaccionan.

Anatomia de un evento logistico

Un evento logistico bien disenado tiene una estructura que captura no solo que paso, 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 diseno:

Eventos inmutables y auto-contenidos. El evento contiene toda la informacion necesaria para procesarse. Un consumidor no deberia necesitar llamar al API de envios para saber que documentos faltan. Si el evento lo incluye, el procesamiento es local, rapido y resiliente a fallos de otros servicios.

Tipado estricto. shipment.customs.held es especifico. No shipment.updated con un campo generico 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 envio, el correlation ID es el hilo conductor.

Metadata de negocio. serviceLevel: "express" no es un detalle tecnico; es informacion que determina la prioridad de respuesta ante la excepcion. Un envio express retenido en aduana activa un flujo diferente (mas urgente, mas caro) que uno estandar.

Topologia de eventos para logistica

La organizacion 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 tentacion es crear un unico 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.

Gestion de excepciones automatizada

Aqui es donde la arquitectura event-driven brilla en logistica. Las excepciones no son solo eventos pasivos que se registran; son triggers que activan flujos de compensacion automaticos.

Patron: Saga para incidencias de aduana

Cuando un envio queda retenido en aduana, se activa una saga (secuencia de pasos con compensacion):

  1. Evento: shipment.customs.held con razon DOCUMENTATION_INCOMPLETE.
  2. Accion: El servicio de documentacion verifica que documentos tiene y cuales faltan. Si los tiene en el sistema pero no se enviaron, los reenvia automaticamente.
  3. Accion: El servicio de notificaciones avisa al cliente con los documentos faltantes y un plazo para aportarlos.
  4. Accion: El servicio de planificacion recalcula la ETA basandose en el retraso estimado y actualiza todos los envios encadenados.
  5. Timeout: Si en 24 horas el cliente no aporta los documentos, escalar a un agente humano.
  6. Compensacion: Si la aduana rechaza definitivamente, activar flujo de devolucion o almacenamiento en deposito temporal.

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

Re-enrutamiento automatico

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 multiples sistemas, tome decisiones y actualice manualmente. Con eventos, el flujo se ejecuta automaticamente. El operador humano revisa y valida, pero no ejecuta.

Integracion con TMS y WMS legacy

El elefante en la habitacion. La mayoria de las empresas logisticas operan con un TMS (Transportation Management System) y/o un WMS (Warehouse Management System) que tiene 10-20 anos, no expone eventos, y funciona con procesos batch nocturnos.

Hay que convivir con ellos. Reemplazarlos es un proyecto de millones y anos. 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 tendran 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 especifico para cada sistema y es donde se concentra el trabajo de integracion.

Problemas reales con CDC en logistica:

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 informacion de forma diferente a como la necesitas. El evento puede necesitar enriquecer datos de otra fuente (master data de clientes, tarifas) antes de ser util. 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 senalizacion 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 via 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 critico es que el polling service sea resiliente: mantenga un cursor de ultimo cambio procesado y se recupere de fallos sin perder ni duplicar eventos.

Patrones de procesamiento

Event Sourcing para trazabilidad

En logistica, la trazabilidad no es un nice-to-have; es un requisito legal (especialmente para mercancias peligrosas, productos alimentarios, y comercio internacional). Event sourcing almacena el estado del envio 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 envio. Si alguien pregunta “por que se retraso este envio?”, la respuesta esta en la secuencia de eventos, no en un campo delay_reason que alguien rellenase manualmente (o no).

CQRS para consultas operativas

El patron CQRS (Command Query Responsibility Segregation) separa las escrituras (eventos) de las lecturas (vistas materializadas). Es especialmente util en logistica 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 envios en transito, 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 asincricamente por los eventos) sirve las consultas operativas sin impactar el rendimiento del pipeline de eventos.

Eleccion de broker

Apache Kafka es el estandar de facto para event streaming en logistica. Razones: throughput alto (millones de eventos/segundo), retencion configurable (los eventos se almacenan, no desaparecen tras consumirlos), garantias de orden dentro de una particion, 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 mas faciles de operar si ya estas 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 (multiples consumidores procesan el mismo evento). Si tu caso es mas “enviar tareas a workers” que “publicar eventos para quien quiera escuchar”, RabbitMQ es mas sencillo.

Para operaciones logisticas con multiples sistemas consumiendo los mismos eventos, Kafka es la eleccion correcta. La inversion en operativa se justifica con la flexibilidad y el rendimiento.

Metricas y observabilidad

Una arquitectura event-driven sin observabilidad es una caja negra. Las metricas criticas:

  • Consumer lag: Cuantos eventos hay sin procesar por cada consumidor. Si el lag crece, el consumidor no procesa al ritmo de produccion. Es la metrica mas importante.
  • End-to-end latency: Tiempo desde que el evento se produce hasta que el ultimo consumidor lo procesa. Para notificaciones al cliente, deberia ser < 30 segundos.
  • Error rate por topic: Eventos que fallan en procesamiento. Una tasa de error > 1% indica un problema sistematico.
  • 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 metricas de aplicacion. Alertas basadas en consumer lag y DLQ size son las dos que merecen despertar a alguien a las 3 de la manana.

Empezar pequeno

No necesitas migrar toda tu operativa a event-driven de golpe. El patron 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 envio.
  3. Publicar eventos en Kafka.
  4. Construir el servicio de notificaciones como consumidor de eventos.
  5. Medir: Latencia de notificacion, tasa de entrega, reduccion de llamadas al call center.
  6. Expandir: Anadir flujos de excepciones automatizadas, re-enrutamiento, facturacion.

El valor del primer flujo es demostrar que la arquitectura funciona y cuantificar el impacto. Con datos reales, es mucho mas facil justificar la inversion para expandir a los flujos siguientes.

Si te interesa el tracking de envios en tiempo real como caso de uso concreto, detallamos la arquitectura en nuestro articulo sobre tracking de envios en tiempo real. La logistica no necesita ser time-consuming y manual. Los eventos estan 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.