Componer microservicios
Con los service bindings baratos y RPC ergonómico, descomponer deja de ser una decisión cara y pasa a ser barata. Cómo partir una app en Workers especializados —autenticación, imágenes, API— que se llaman entre sí, cómo diseñar una topología de gateway y especialistas, y por qué el verdadero arte no es cablearlos sino decidir qué merece ser su propio Worker.
Cuando comunicar dos Workers es casi gratis y llamarlos se siente como invocar un método local, descomponer una aplicación deja de ser una decisión arquitectónica cara y se vuelve una elección casi organizativa. Puedes partir tu sistema en Workers pequeños y especializados —uno que autentica, otro que procesa imágenes, uno que orquesta la API— cada uno desplegado, escalado y razonado por separado, y cableados entre sí por bindings. Pero que sea barato hacerlo no significa que hacerlo siempre convenga: este es el arte de trazar las fronteras.
- Identificar por qué costuras conviene partir un Worker y por cuáles no.
- Diseñar una topología de gateway y especialistas cableada con service bindings.
- Leer el grafo de llamadas como el mapa real de la arquitectura del sistema.
- Reconocer y evitar el monolito distribuido: acoplamiento sin independencia.
Cuándo partir un Worker
La tentación, en cuanto descomponer es barato, es descomponer por moda: un Worker por cada sustantivo del dominio. Es un error. Se parte por costuras reales, y las que importan son cuatro. Por ritmo de cambio: aísla lo que cambia a diario de lo que cambia una vez al año. Por responsabilidad: una pieza, un motivo para existir. Por perfil de escalado: el redimensionado de imágenes, intensivo en CPU, no debería compartir destino con un endpoint ligero que se llama mil veces por segundo. Y por recurso: un Worker que necesita un binding pesado o un permiso delicado se aísla para que solo él lo tenga.
Si dos supuestas piezas siempre cambian juntas, se despliegan juntas y escalan igual, no son dos piezas: son una a la que le pusiste una costura falsa. Partirlas solo te regala la latencia y la complejidad de la distribución sin ninguna de sus ventajas.
Una topología: gateway y especialistas
El patrón más común es un Worker de entrada —el gateway— que recibe el tráfico público y orquesta a un puñado de especialistas privados sin ruta pública. El gateway autentica llamando a AUTH, transforma imágenes llamando a IMG y consulta datos llamando a API, y compone la respuesta final.
{
"name": "gateway",
"services": [
{ "binding": "AUTH", "service": "auth-worker", "entrypoint": "AuthService" },
{ "binding": "IMG", "service": "img-worker", "entrypoint": "ImageService" },
{ "binding": "API", "service": "api-worker", "entrypoint": "ApiService" }
]
}
export default {
async fetch(request, env): Promise<Response> {
const { valido, usuario } = await env.AUTH.verificar(
request.headers.get("authorization") ?? "",
);
if (!valido) return new Response("no autorizado", { status: 401 });
// los especialistas independientes se invocan en paralelo
const [perfil, miniatura] = await Promise.all([
env.API.perfilDe(usuario),
env.IMG.miniatura(usuario),
]);
return Response.json({ perfil, miniatura });
},
} satisfies ExportedHandler<Env>;
Fíjate en el Promise.all: como los dos especialistas no dependen uno del otro, se llaman a la vez. Encadenar llamadas que podrían ir en paralelo es el modo más fácil de tirar por la borda la velocidad que el binding te regaló.
El grafo de llamadas es la arquitectura
Como cada vínculo se declara en la sección services, la unión de todas las wrangler.jsonc de tu sistema es el diagrama de arquitectura: no hay llamadas ocultas tras una URL escondida en el código. Esa legibilidad es un regalo, pero también una responsabilidad, porque el grafo puede degenerar. Mantenlo como un grafo dirigido y sin ciclos: si A llama a B y B llama a A, has creado un abrazo mortal de despliegue del que ninguno sale sin el otro. Y mantenlo poco profundo: cada salto, por barato que sea, suma su latencia, y una cadena de cinco Workers en fila reconstruye la lentitud que creías haber eliminado.
Gateway
El único con ruta pública. Recibe el tráfico, autentica y orquesta. Los demás existen solo tras él, invisibles desde internet.
Especialistas
Un Worker por responsabilidad real, con su propio binding pesado si lo necesita. Sin ruta pública: solo el gateway u otro especialista los alcanza.
Grafo sano
Dirigido, sin ciclos y poco profundo. Legible en las configs. Cada arista es una dependencia que alguien tendrá que versionar.
flowchart TD U[internet] --> G[gateway con ruta publica] G -->|verificar| A[auth service] G -->|miniatura| I[image service] G -->|datos| P[api service] P --> DB[D1] I --> R2[R2 imagenes] style G fill:#89b4fa,color:#11111b style A fill:#a6e3a1,color:#11111b style I fill:#a6e3a1,color:#11111b style P fill:#a6e3a1,color:#11111b
Los service bindings eliminan el coste mecánico de partir una aplicación —la red, la latencia, las credenciales entre piezas— pero son incapaces de eliminar su coste cognitivo, y confundir ambos es la trampa en la que caen los equipos deslumbrados por lo fácil que se volvió cablear Workers. Aunque la llamada se sienta local, un sistema partido en muchos Workers sigue siendo un sistema distribuido: los fallos se componen, la latencia se acumula salto a salto, y un cambio en una pieza puede propagarse por el grafo de maneras que ningún compilador ve entero. Cuando descomponer era caro, ese coste actuaba como un freno natural que te obligaba a partir solo cuando de verdad valía la pena. Al retirar el freno, la plataforma te entrega una libertad que puede volverse contra ti: la de fabricar un monolito distribuido, ese peor de los mundos en que tienes veinte Workers tan acoplados que hay que desplegarlos en bloque —toda la fricción de la distribución y ninguna de su independencia—. El antídoto no es técnico sino de criterio. Parte por costuras de cambio y de escalado independientes, no por sustantivos del dominio; si dos piezas siempre cambian y se despliegan juntas, son una sola disfrazada. Deja que el grafo de bindings sea un árbol poco profundo que refleje fronteras reales, no una telaraña que refleje tu entusiasmo. Porque la habilidad que de verdad se te pide ya no es unir Workers —eso la plataforma lo volvió trivial— sino la mucho más difícil de decidir qué merece ser un Worker. La arquitectura nunca fue el cableado; siempre fue el trazado de las fronteras, y ahora que lo primero es gratis, lo segundo es lo único que queda por dominar.
- Toma un Worker monolítico que autentique, sirva datos y procese imágenes, y decide qué costuras justifican partirlo según ritmo de cambio, responsabilidad, escalado y recurso.
- Extrae un
auth-workery unimg-workercomo servicios sin ruta pública y deja ungatewaycomo único Worker con ruta pública. - Cablea los bindings en la
wrangler.jsoncdel gateway y orquesta las llamadas; usaPromise.allpara las que sean independientes. - Dibuja el grafo de llamadas leyendo solo las secciones
servicesy verifica que es dirigido, sin ciclos y poco profundo. - Encuentra en tu diseño dos piezas que siempre cambiarían juntas y arguméntalo: ¿son de verdad dos Workers o un monolito distribuido disfrazado?