Solución del reto Global Dispatch (Guatemaltek): un sistema que simula cómo NewCron conecta transportistas de car haulers con predios de automóviles en Estados Unidos, usando Solace PubSub+ como bróker de mensajería.
Tres programas independientes (Spring Boot), cada uno una pieza del flujo descrito en el reto:
| Programa | Rol | Qué hace |
|---|---|---|
order-intake |
Productor | Recibe el payload de una solicitud de carga (archivo .json), valida las reglas de fechas de NewCron y publica el resultado en Solace |
carrier-dashboard |
Consumidor | Simula el panel de transportistas: muestra en tiempo real las cargas disponibles y permite "tomar" una desde la consola |
client-status |
Consumidor | Simula el apartado del cliente: muestra en tiempo real si su solicitud fue Accepted o Cancelled, y por qué |
pickupDateno puede ser anterior a la fecha actual.- Si
pickupDatees hoy, la solicitud no puede llegar después de las 3:00 p.m. deliveryDatedebe ser mayor apickupDate, con al menos un día de diferencia.
Si alguna regla falla, se genera un payload Cancelled con la nota correspondiente
(igual al ejemplo del documento). Si todas pasan, el pedido se publica para los
transportistas y se genera de inmediato un payload Accepted (esto refleja
exactamente el ejemplo del documento: la nota dice que el cliente recibirá un
correo cuando un transportista acepte, es decir, Accepted = "tu solicitud fue
aceptada por el sistema", no = "un transportista ya la tomó". La toma real de
la carga en el dashboard es una simulación operativa aparte, sin payload propio,
porque el documento no define uno para ese evento).
publica pedido válido consume
order-intake ───────────────────────────► orders/new ────────► carrier-dashboard
│ (tópico) (cola: orders-pending-q)
│
│ publica Accepted / Cancelled consume
└───────────────────────────────────► orders/result ───────► client-status
(tópico) (cola: orders-results-q)
- Se usan tópicos para publicar (
orders/new,orders/result) y colas durables para consumir (orders-pending-q,orders-results-q), mapeadas al tópico correspondiente ("Topic to Queue Mapping", patrón estándar de Solace). Las colas se crean automáticamente al arrancar cada consumidor (session.provision(...)+session.addSubscription(...)), no hace falta crearlas a mano en la consola. - Usar tópico + cola (en vez de publicar directo a una cola) permite, si quisieras extenderlo, tener varios dashboards o varios clientes escuchando al mismo tiempo sin tocar el productor.
- JDK 17+
- Maven 3.9+
- Una cuenta y un servicio de Solace Cloud (o Solace PubSub+ Software si prefieres correrlo local con Docker)
- Entra a console.solace.cloud y crea una cuenta (hay plan gratuito).
- En Cluster Manager, haz clic en Create Service, elige el plan gratuito y una región cercana.
- Cuando el servicio esté "Running", entra a Manage → pestaña Connect.
- Copia estos cuatro datos (los necesitarás en el paso 4):
- SMF Host (algo como
tcps://xxxxx.messaging.solace.cloud:55443, o usa el puerto55555sin TLS si prefieres) - Message VPN
- Username
- Password
- SMF Host (algo como
Cada módulo lee la configuración de variables de entorno (con valores por
defecto en application.properties). Antes de correr cualquier programa,
exporta:
export SOLACE_HOST="tcp://xxxxx.messaging.solace.cloud:55555"
export SOLACE_VPN="tu-vpn"
export SOLACE_USERNAME="tu-usuario"
export SOLACE_PASSWORD="tu-password"Desde la raíz global-dispatch/:
mvn clean installEsto compila los cuatro módulos (common, order-intake, carrier-dashboard,
client-status) y corre las pruebas unitarias de DateValidator.
Abre tres terminales (con las variables de entorno del paso 4 ya exportadas en cada una).
Terminal 1 — Dashboard de transportistas (déjalo corriendo):
cd carrier-dashboard
mvn spring-boot:runTerminal 2 — Estado del cliente (déjalo corriendo):
cd client-status
mvn spring-boot:runTerminal 3 — Enviar una solicitud:
cd order-intake
mvn spring-boot:run -Dspring-boot.run.arguments=../order-intake/sample-payloads/valid.jsonVerás:
- En la Terminal 1: la carga nueva apareciendo, y puedes escribir
6600111para "tomarla". - En la Terminal 2:
[ACCEPTED] Pedido #6600111 -> You will receive an email when a carrier accepts this dispatch request
Nota: los
.jsonde ejemplo enorder-intake/sample-payloads/tienen fechas fijas. Antes de probar, ajustapickupDate/deliveryDatepara que sean coherentes con la fecha real en que corras la prueba.
mvn spring-boot:run -Dspring-boot.run.arguments=../order-intake/sample-payloads/invalid-pickup-in-past.json
mvn spring-boot:run -Dspring-boot.run.arguments=../order-intake/sample-payloads/invalid-delivery-too-soon.jsonEn la Terminal 2 (Client Status) deberías ver ambos como [CANCELLED], con el
motivo correspondiente ("Pickup date cannot be earlier than the current date."
/ "Delivery date must be at least one day after the pickup date.") y no
deberían aparecer en el dashboard de transportistas (Terminal 1), porque
nunca se publican en orders/new.
También puedes correr solo las pruebas unitarias de las reglas de negocio, sin necesidad de un broker Solace:
cd common
mvn testglobal-dispatch/
├── pom.xml # POM padre (agrega los 4 módulos)
├── README.md
├── common/ # Modelos, validación y utilidades Solace compartidas
│ └── src/main/java/com/newcron/dispatch/common/
│ ├── model/ (ShipperOrderRequest, Stop, Vehicle, OrderResult)
│ ├── validation/ DateValidator.java (+ tests)
│ └── solace/ SolaceSessionFactory, JsonMessageCodec, DurableQueueProvisioner
├── order-intake/ # Productor: valida y publica
│ └── sample-payloads/ Payloads .json de ejemplo (válido y dos casos inválidos)
├── carrier-dashboard/ # Consumidor: dashboard de transportistas
└── client-status/ # Consumidor: estado para el cliente