Índice del curso
Lección 08 — El router
Por Dorian Chávez · cofundador y Chief Executive Officer de Hábil ·
Lo que vas a lograr: partir tu integración en dos servicios que corren por separado: un router que recibe la orden y un servicio int-rest que sabe hablar con la API. Escribes la llamada que los une, por el nombre del servicio y nunca por una dirección.
Antes de empezar necesitas: las lecciones 02 a 07 terminadas y el ambiente de la 00 encendido:
docker compose -f infra/compose.yaml up -d --wait # desde la carpeta raíz de la práctica
Lo nuevo de hoy, en una frase
Hasta ahora cada lección era una ruta que hacía todo: recibir, traducir y entregar. Con tres destinos (la API, el ERP y el socio), esa ruta crecería sin parar. Hoy la partes: un servicio decide a dónde va la orden y otro sabe entregarla. Cada uno corre en su propio contenedor.
tienda ──POST /ordenes──▶ [orden-router] ──POST http://int-rest:8080/integrar──▶ [int-rest] ──▶ API de pedidos
La imagen: la recepción de un hospital. La recepcionista no opera a nadie: pregunta qué te pasa y te manda con el especialista. Si mañana abre una especialidad nueva, la recepción sigue igual; solo aprende una puerta más.
1. Prepara tu carpeta
cd lecciones/08-el-router
cp -R inicio mio
Mira lo que copiaste: mio/ tiene dos carpetas, una por servicio.
| Carpeta | Servicio | Qué trae |
|---|---|---|
mio/orden-router/ |
El router | Su ruta: recibe la orden de la tienda y se la pasa a int-rest |
mio/int-rest/ |
int-rest | Su ruta y el mapeo v2 de la 03: traduce la orden y la entrega a la API |
2. Lee el compose de la lección
Esta lección no usa herramientas/camel. Los dos servicios se levantan juntos con su propio compose.yaml, como el
ambiente de la 00. Ábrelo:
x-camel: &camel
image: apache/camel-jbang:4.22.1
command: ["run", "."] # corre todos los *.camel.yaml de su carpeta
services:
orden-router:
<<: *camel
volumes:
- ./${DONDE:-mio}/orden-router:/work
ports: ["8080:8080"]
int-rest:
<<: *camel
volumes:
- ./${DONDE:-mio}/int-rest:/work
# sin ports: no se ve desde tu máquina; por dentro de la red, cualquier servicio lo alcanza
Dos cosas que vale la pena ver:
- Los dos servicios son la misma imagen de Camel. Lo único distinto es la carpeta que cada uno monta.
&cameldefine la parte común una vez y<<: *camella reutiliza. Para crear un servicio nuevo no hay que programar otro programa: basta otra carpeta con sus rutas. - Solo el router tiene
ports. Es el único que se ve desde tu máquina. int-rest no se ve desde afuera, pero dentro de la red de Docker cualquier servicio puede llamarlo; en esta práctica, el único que lo llama es el router.
DONDE es una variable que dice qué carpetas montar: mio (la tuya, por omisión) o solucion (la nuestra).
3. Lee las dos rutas
mio/int-rest/int-rest.camel.yaml ya viene completa. Recibe en /integrar, traduce con el mapeo v2 y llama a
la API, igual que la ruta de la 03.
mio/orden-router/orden-router.camel.yaml recibe en /ordenes y prepara la orden para pasarla. Antes de la
llamada le pone unos encabezados que empiezan con ce-:
| Encabezado | Valor para la orden 1003 |
|---|---|
ce-specversion |
1.0 |
ce-type |
mx.tienda.orden.creada |
ce-source |
/tiendas/T-022 |
ce-subject |
1003 |
ce-id |
orden-1003-creada |
Son el sobre de la orden, en un formato estándar que se llama CloudEvents: dicen qué pasó, dónde y a quién. Hoy solo los vas a mirar viajar; en la 09 sirven para seguir una orden de servicio en servicio.
Al final de la ruta hay un TODO: falta la llamada a int-rest.
4. Predice
Antes de escribir la llamada, contesta en un papel:
- Si mandas la orden 1003 al router tal como está, sin la llamada, ¿qué le contesta a la tienda? ¿Llega algo a la API?
- Cuando ya esté la llamada, ¿en qué orden esperas ver pasar la orden por los logs de los dos servicios?
- Dentro del contenedor del router, ¿a qué máquina crees que apunta
localhost?
5. Escribe la llamada
En el lugar del TODO, agrega un paso to que haga un POST a la ruta /integrar de int-rest, en su puerto 8080.
Llámalo por su nombre de servicio, int-rest, como int-rest llama a la API por api-pedidos. Usa
throwExceptionOnFailure: false para que, si int-rest contesta con error, el router lo anote en lugar de tronar:
la siguiente línea del log dice qué contestó.
Si te atoras, la llamada de int-rest a la API tiene exactamente la misma forma.
6. Levántalo y mándale una orden
Desde la carpeta de la lección:
DONDE=mio docker compose -f compose.yaml up -d
DONDE=mio docker compose -f compose.yaml logs -f
El segundo comando muestra los logs de los dos servicios juntos, cada renglón con el nombre del que lo escribió.
Espera a ver Started orden-router y Started int-rest. En otra terminal, desde la raíz de la práctica:
curl -i -XPOST localhost:8080/ordenes -H 'Content-Type: application/json' -d @datos/ordenes/orden-1003.json
En los logs deberías ver la orden pasar por los dos servicios:
orden-router-1 | Orden 1003: se la paso a int-rest
int-rest-1 | Recibí del router la orden 1003 (sobre orden-1003-creada, tipo mx.tienda.orden.creada)
int-rest-1 | La API contestó 201 para la orden 1003
orden-router-1 | Orden 1003: int-rest contestó 201
¿Coincide con tu predicción 2? La respuesta regresa por el mismo camino: la API le contesta a int-rest, int-rest al router y el router a la tienda.
Si la API contesta 200 en vez de 201, ya tenía la orden 1003 de la lección 03. Reiníciala para que empiece vacía:
docker compose -f infra/compose.yaml restart api-pedidos.
7. Rómpelo a propósito: localhost
Ahora la pregunta 3. En la ruta del router, cambia int-rest por localhost:
uri: http://localhost:8080/integrar
Apaga y vuelve a levantar los servicios para que tomen el cambio, y manda la orden otra vez:
DONDE=mio docker compose -f compose.yaml down
DONDE=mio docker compose -f compose.yaml up -d
La tienda recibe un 404 y el log del router dice int-rest contestó 404. Pero int-rest nunca recibió nada.
Dentro de un contenedor, localhost es ese mismo contenedor. El router se llamó a sí mismo, buscó una ruta
/integrar que él no tiene y se contestó «no existe». En tu máquina, localhost es tu máquina; en cada contenedor,
es el contenedor.
Por eso los servicios se llaman por su nombre: Docker sabe en qué contenedor vive int-rest, aunque mañana
cambie de dirección. Regresa el nombre y apaga:
DONDE=mio docker compose -f compose.yaml down
8. Comprueba
./verifica
verifica reinicia la API para que empiece vacía, levanta los dos servicios, manda la orden 1003 y al final los
apaga. Está terminada cuando ves las seis marcas verdes:
✅ El router arrancó
✅ int-rest arrancó (la misma imagen, otra carpeta)
✅ La tienda mandó la 1003 al router y la respuesta final fue 201
✅ La API registró la orden 1003: el recorrido tienda → router → int-rest → API se completó
✅ int-rest recibió el sobre CloudEvents de la orden (id orden-1003-creada)
✅ int-rest no se ve desde tu máquina: solo el router lo alcanza
La cuarta la confirma la API desde su propio registro: no basta con que el router diga que pasó la orden.
¿No te corre? ./verifica solucion prueba la nuestra, y
diff mio/orden-router/orden-router.camel.yaml solucion/orden-router/orden-router.camel.yaml te dice en qué son
distintas.
| Lo que ves | Qué haces |
|---|---|
La tienda esperaba 201 y le llegó 200 |
El router devolvió la orden sin llamar a nadie: falta el paso to |
La tienda esperaba 201 y le llegó 404 |
La llamada apunta a localhost: vuelve al paso 7 |
El puerto 8080 está ocupado |
Quedó otra ruta corriendo, o los servicios de esta lección: DONDE=mio docker compose -f compose.yaml down |
El router no arrancó |
verifica muestra las últimas líneas del log: casi siempre es la sangría del paso nuevo |
Ejercicio: un especialista más
Sin escribir código, contesta en un papel: si mañana quisieras un servicio int-erp que entregue la orden al ERP
con la ruta de la 05, ¿qué carpeta crearías, qué pondrías en ella y qué renglones agregarías al compose.yaml?
¿Le pondrías ports? ¿Qué cambiaría en el router?
Por qué se hizo así
Lo que decidimos en esta lección, con lo que descartamos y qué nos haría cambiar de idea:
- Una sola imagen; cada servicio es una carpeta
- Los servicios se hablan por nombre, sobre HTTP
- CloudEvents en modo binario como sobre
Repaso
- ¿Qué hace el router y qué hace int-rest? ¿Por qué conviene separarlos?
- Si los dos servicios usan la misma imagen, ¿qué los hace distintos?
- ¿Por qué el router llama a
int-resty no alocalhost? - ¿Por qué int-rest no tiene
portsen el compose? - ¿Qué dicen los encabezados
ce-type,ce-subjectyce-idde la orden 1003?
Siguiente: en la lección 09 sigues una orden de servicio en servicio con el sobre CloudEvents, y descubres qué pasa cuando un servicio no lo reenvía.
¿Prefiere correo? Escríbanos a hola@habil.mx