Hoy vamos a hablar de la arquitectura de microservicios y la compararemos con la monolítica, después como en otras ocasiones escribiremos un poco de código, lo conectaremos todo con un api gateway, es esta ocasión con Kong, y lo veremos en funcionamiento. Este estilo arquitectónico divide una aplicación en un conjunto de servicios pequeños e independientes que se pueden desplegar de forma separada. Cada uno de estos microservicios es responsable de una tarea del negocio y que se comunican entre sí normalmente en una red. Esta independencia y el tamaño manejable, siempre les ha dado un prestigio de entornos fáciles de mantener, escalar, actualizar y desplegar. Veremos si realmente son tantos los beneficios y los inconvenientes que tiene.
La técnica consiste en que cada microservicio encapsula una necesidad de negocio concreta, por ejemplo “gestión de pedidos”, “Facturación”o “revisión de stock”. Cada microservicio tiene su propio ciclo de vida y su propio método de despliegue. Si una de las piezas requiere un escalado independiente es completamente factible, incluso su propia base de datos con lo cual el acoplamiento se va reducir al mínimo.
El sistema está descentralizado con lo cual cada equipo puede elegir su propio stack tecnológico, y cada uno de los servicios gestiona su propia persistencia. Existe por tanto una mínima centralización de los servicios, en el sentido de que cada uno es autogestionado y corre en su propio proceso. La separación permite aislar determinados fallos y limitar su porpagación, aunque no elimina las dependencias entre servicios. Un servicio que depende de otro puede seguir viéndose afectado por su indisponibilidad, por lo que son necesarias estrategias de resiliencia como timeouts o retries.
La comunicación es a través de la red, no existe una persistencia en memoria, puede hacerse de manera síncrona con peticiones ligeras HTTP como REST o asíncrona a través de sistemas de mensajería como Kafka o RabbitMQ o por eventos.
Martin Fowler y James Lewis popularizaron el nombre en 2014 en un post https://martinfowler.com/articles/microservices.html, que vale la pena leer.
Para poder definir la arquitectura de microservicios podemos enfrentarlo a la arquitectura monolítica. Infinidad de aplicaciones se van convirtiendo en un gran “mole de piedra” en la que todo el backend se ejecuta dentro del mismo proceso, conectado a una misma base de datos, obligando a que cada cambio, por pequeño que sea, fuerce a una actualización de todo el sistema. Esta actualización a su vez obligará a una suspensión de todo el servicio. Por otro lado el despliegue se ejecuta en una sola acción y el testing puede ser desarrollado también en una sola acción, de manera total, parcial o selectiva. El Monolito se puede escalar tanto horizontalmente instanciándolo en diversas entidades o verticalmente haciendo la máquina más potente. Si se clona las entidades para aumentar la disponibilidad de uno de los servicios bastaría con anteponer un balanceador de carga. Esto también tiene la contrapartida de estar haciendo un gasto en máquinas por una única pieza. En definitiva el monolito se mantiene bien en un principio, pero a la larga el desarrollo se convierte en un pequeño infierno.
Volviendo a los microservicios, es preciso establecer cómo se va a proceder a la fragmentación de la aplicación en componentes. La división basada en microservicios se realiza en unidades lógicas de negocio, en lugar de basarse en capas tecnológicas más propias de un entorno monolítico, como la capa de negocio, la capa de interfaz de usuario o la capa de base de datos.
Robert C. Martin, conocido como Uncle Bob, definió un componente como una unidad de software que puede reemplazarse y actualizarse de forma independiente. La forma natural de modularizar una arquitectura de microservicios es, precisamente, a través de servicios. Esto contrasta con las librerías, que son componentes invocados mediante llamadas de función en memoria, mientras que los servicios se comunican a través de la red, ya sea mediante peticiones HTTP o llamadas a procedimiento remoto (RPC).
Ventajas de los microservicios frente a una aplicación monolítica basada en librerías
Como ya mencioné antes, la posibilidad de actualizar un servicio de forma independiente es una de las principales ventajas de esta arquitectura. Al realizar un cambio, pueden darse dos escenarios: que sea puramente interno, y no tenga implicaciones fuera del propio servicio, o que afecte a la interfaz y a su contrato, en cuyo caso repercutirá sobre los servicios que dependen de él. En este segundo escenario será necesaria una coordinación explícita entre los equipos para planificar los distintos despliegues.
Otro concepto relevante es la exposición de los métodos frente a la disponibilidad de los endpoints que tiene implicaciones importantes. Lo que en arquitectura monolítica las llamadas dependían únicamente de la disciplina del programador y de la documentación, en los microservicios queda protegido por las propias restricciones técnicas de la arquitectura basada en endpoints.
La ejecución de las llamadas a los servicios es más costosa que la instanciación de los métodos dentro de una librería, o dicho de otro modo, las llamadas a distancia siempre han sido mas caras que la llamadas locales. Esta es un clara ventaja de los diseños monolíticos, y aun hay más inconvenientes. La granularidad de una llamada a un servicio es mas gruesa que la invocación de los métodos de una librería, es decir, a diferencia de la llamada de un metodo de una librería que es prácticamente gratis, la llamada a un servicio implica unos compromisos de latencia, serializacion y posibilidad de fallo. Por eso conviene agrupar varias operaciones pequeñas en una sola llamada perdiend detalle.
Pero es importante remarcar un detalle por el hecho de usar llamadas remotas a una API no estamos exentos del riesgo del acoplamiento. Se pueden incurrir en acoplamientos en el sentido de tener que hacer múltiples llamadas para completar una operación o tener que conocer detalles internos perdiendo el encapsulamiento. Buscar un diseño de grano fino obligaría al cliente a saber en qué orden se hacen las llamadas internamente, y haciéndolas consecutivamente en lugar de una única llamada pidiendo lo que se necesita. Así es que un diseño de grano grueso, como efecto secundario tambien reduce ese acoplamiento de cliente entre cliente y servicio, no es intencionado, es una consecuencia de tratar de hacer mínimo numero de llamadas en la red por una cuestión de rendimiento.
Puesto que la red no es un canal fiable, la arquitectura debe diseñarse asumiendo que los fallos van a ocurrir: cualquier servicio debe ser capaz de afrontar un fallo de red, minimizando su impacto y respondiendo de la forma más eficaz posible. En definitiva, se busca la resiliencia del servicio.
Esta necesidad de tolerar fallos trae consigo otra propiedad igual de importante: la idempotencia. Supongamos que se ha implementado un mecanismo de reintentos, por ejemplo, hasta 3 intentos ante un fallo de red. En ese escenario puede darse el caso de que no tengamos certeza de si la petición original llegó a ejecutarse, y que las llamadas repetidas terminen corrompiendo los datos. Por este motivo, las operaciones de escritura deben diseñarse de modo que ejecuciones sucesivas produzcan siempre el mismo resultado y dejen el sistema en el mismo estado final, sin importar cuántas veces se repitan.
La monitorización juega un papel importante también. Podemos hablar de dos tipos, por un lado la monitorización técnica, y por otro la monitorización semántica.
La monitorizacion técnica se enfoca en revisar aspectos valga la redundancia, más técnicos. ¿Está la API en pie? ¿Responde el Health? ¿Los rangos de uso de la CPU y la memoria están dentro de lo razonable? La latencia es aceptable?
Por otro la monitorización semántica que se encarga de comprobar que los pedidos se están completando. Se comprueba que la tasa de pedidos cerrados vs a los rechazados es normal, es decir si el flujo completo de compra funciona E2E.
Pero todavía queda una cuestión importante, ¿cómo despliego esto, como nos conectamos a los microservicios al api gateway? ¿dejo que vayan por libre? Nos falta un concepto clave que es el de la coordinación de todas las operaciones. Para esto hay dos grandes enfoques para gestionar la comunicación entre servicios: orquestación y coreografía. La diferencia fundamental está en dónde reside el control del flujo de trabajo.
Orquestación funciona como una orquesta dirigida por un director: existe un componente central (un orquestador) que conoce todo el proceso de principio a fin y va indicando a cada servicio qué hacer y cuándo hacerlo. Este orquestador, que puede ser un servicio dedicado o una herramienta como Camunda, Temporal o AWS Step Functions, llama explícitamente a cada microservicio, espera su respuesta y decide el siguiente paso según el resultado. Esto facilita mucho la visibilidad del proceso completo, el manejo de errores y la trazabilidad, porque todo el flujo está centralizado y es fácil de entender con solo mirar un lugar. Sin embargo, introduce un punto único de acoplamiento y de fallo: si el orquestador cae o tiene un problema, todo el flujo se detiene, y además los servicios individuales dependen de que el orquestador les diga qué hacer, lo cual reduce su autonomía.
Coreografía, en cambio, es como un grupo de bailarines que se mueven de forma coordinada sin un director que les dé órdenes en tiempo real: cada uno conoce su parte y reacciona a las señales de los demás. En términos técnicos, cada microservicio publica eventos cuando algo relevante ocurre (por ejemplo, "pedido creado") y otros servicios están suscritos a esos eventos y reaccionan de forma autónoma, sin que nadie les diga explícitamente qué hacer. Esto normalmente se implementa con un bus de eventos o sistema de mensajería (Kafka, RabbitMQ, etc.). La gran ventaja es el bajo acoplamiento: los servicios no necesitan conocerse entre sí, solo saber qué eventos consumir y publicar, lo que facilita escalar el sistema y añadir nuevos servicios sin tocar los existentes. La desventaja es que, al no haber un lugar centralizado que muestre el flujo completo, puede volverse difícil de rastrear y depurar cuando el proceso involucra muchos pasos y servicios, ya que la lógica del negocio queda "distribuida" implícitamente entre las reacciones de cada servicio a los eventos.
En fin, no tocaré mas este tema de la orquestación por que dará artículo en el que hablaremos de ella y probaremos algún orquestador.
Así que vamos ya tirar un poco de código y poner algunos microservicios a funcionar. Como dije todo va articulado desde un Kong que será punto de entrada a los microservicios
_format_version: "3.0"
services:
- name: order-service
url: http://order-service:8080
routes:
- name: orders-route
paths:
- /orders
strip_path: false
- name: order-health-route
paths:
- /health
strip_path: false
- name: stock-service
url: http://stock-service:8081
routes:
- name: stock-route
paths:
- /stock
- /products
strip_path: false
- name: notification-service
url: http://notification-service:8082
routes:
- name: notifications-route
paths:
- /v1/notifications
strip_path: false
Aqui os dejo el docker-compose.yml donde se van a montar lops diferentes servicios. En este pequeño escenario vamos a tener tres contenedores, cada uno con su stack, uno con Java, otro con Nodejs y otro con PHP que irán dialogando cada uno con el resto de microservicios:
services:
stock-service:
build: ./stock-service-php
container_name: stock-service
ports:
- "8081:8081"
healthcheck:
test: ["CMD", "php", "-r", "exit(@file_get_contents('http://localhost:8081/health') ? 0 : 1);"]
interval: 30s
timeout: 3s
retries: 5
start_period: 5s
notification-service:
build: ./notification-service-java
container_name: notification-service
ports:
- "8082:8082"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8082/v1/notifications/health || exit 1"]
interval: 10s
timeout: 3s
retries: 8
start_period: 30s
order-service:
build: ./order-service-node
container_name: order-service
ports:
- "8080:8080"
environment:
STOCK_SERVICE_URL: http://stock-service:8081
NOTIFICATION_SERVICE_URL: http://notification-service:8082
depends_on:
stock-service:
condition: service_healthy
notification-service:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]
interval: 10s
timeout: 3s
retries: 5
start_period: 5s
kong:
image: kong:3.9
container_name: kong
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /opt/kong/kong.yml
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
KONG_ADMIN_LISTEN: 0.0.0.0:8001
KONG_ADMIN_GUI_LISTEN: 0.0.0.0:8002
volumes:
- ./kong/kong.yml:/opt/kong/kong.yml:ro
ports:
- "8000:8000"
- "8001:8001"
- "8002:8002"
depends_on:
order-service:
condition: service_healthy
stock-service:
condition: service_healthy
notification-service:
condition: service_healthy
networks:
default:
name: purchase-pipeline-net
En el siguiente diagrama de secuencia podemos ver como se van ejecutando los microservicos en secuencia. Estan mejor identificados en el Readme.md

Aqui podemos ver el grafico construido con Mermaid que quedaria asi:

Ahora podemos probar algunos de lo microservicios, lo primero revisar el health:
╰─ curl -s http://localhost:8000/health
{"status":"ok","service":"order-service"}
╰─ curl -s http://localhost:8081/health
{"status":"ok","service":"stock-service"}
╰─ curl -s http://localhost:8082/v1/notifications/health
{"status":"ok","service":"notification-service"}
listado de productos:
curl -s http://localhost:8000/products | jq ─╯
{
"products": [
{
"sku": "SKU-TSHIRT",
"name": "Camiseta",
"stock": 20
},
{
"sku": "SKU-MUG",
"name": "Taza",
"stock": 15
},
{
"sku": "SKU-HOODIE",
"name": "Sudadera",
"stock": 8
},
{
"sku": "SKU-CAP",
"name": "Gorra",
"stock": 5
}
]
}
lanzamiento de una orden:
curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:8000/orders \ ─╯
-H 'Content-Type: application/json' \
-d '{
"customerId": "cust-1",
"address": "Calle Demo 1, Madrid",
"items": [
{ "sku": "SKU-TSHIRT", "qty": 2 },
{ "sku": "SKU-MUG", "qty": 1 }
]
}'
{"id":"458e7e37-b5ef-4760-bf4b-3c81106da13d","customerId":"cust-1","address":"Calle Demo 1, Madrid","items":[{"sku":"SKU-TSHIRT","qty":2},{"sku":"SKU-MUG","qty":1}],"status":"CONFIRMED","reservationId":"res-6a9f3738101760.55196510","notifications":[{"id":"0d630d40-77ed-4474-be7f-c2c14c598e59","type":"warehouse","orderId":"458e7e37-b5ef-4760-bf4b-3c81106da13d","payload":{"customerId":"cust-1","items":[{"sku":"SKU-TSHIRT","qty":2},{"sku":"SKU-MUG","qty":1}]},"createdAt":"2026-09-07T22:14:16.174270140Z"},{"id":"a6444d37-83c7-4672-9219-975d8c4baed2","type":"courier","orderId":"458e7e37-b5ef-4760-bf4b-3c81106da13d","payload":{"address":"Calle Demo 1, Madrid","items":[{"sku":"SKU-TSHIRT","qty":2},{"sku":"SKU-MUG","qty":1}]},"createdAt":"2026-09-07T22:14:16.198757057Z"},{"id":"b61c1cb9-0087-4b25-8125-a0b5a837b784","type":"customer","orderId":"458e7e37-b5ef-4760-bf4b-3c81106da13d","payload":{"customerId":"cust-1","status":"CONFIRMED"},"createdAt":"2026-09-07T22:14:16.206403007Z"}],"createdAt":"2026-09-07T22:14:15.982Z"}
Obtencion de una orden con id concreto
curl -s http://localhost:8000/orders/458e7e37-b5ef-4760-bf4b-3c81106da13d
{"id":"458e7e37-b5ef-4760-bf4b-3c81106da13d","customerId":"cust-1","address":"Calle Demo 1, Madrid","items":[{"sku":"SKU-TSHIRT","qty":2},{"sku":"SKU-MUG","qty":1}],"status":"CONFIRMED","reservationId":"res-6a9f3738101760.55196510","notifications":[{"id":"0d630d40-77ed-4474-be7f-c2c14c598e59","type":"warehouse","orderId":"458e7e37-b5ef-4760-bf4b-3c81106da13d","payload":{"customerId":"cust-1","items":[{"sku":"SKU-TSHIRT","qty":2},{"sku":"SKU-MUG","qty":1}]},"createdAt":"2026-09-07T22:14:16.174270140Z"},{"id":"a6444d37-83c7-4672-9219-975d8c4baed2","type":"courier","orderId":"458e7e37-b5ef-4760-bf4b-3c81106da13d","payload":{"address":"Calle Demo 1, Madrid","items":[{"sku":"SKU-TSHIRT","qty":2},{"sku":"SKU-MUG","qty":1}]},"createdAt":"2026-09-07T22:14:16.198757057Z"},{"id":"b61c1cb9-0087-4b25-8125-a0b5a837b784","type":"customer","orderId":"458e7e37-b5ef-4760-bf4b-3c81106da13d","payload":{"customerId":"cust-1","status":"CONFIRMED"},"createdAt":"2026-09-07T22:14:16.206403007Z"}],"createdAt":"2026-09-07T22:14:15.982Z"}%
Orden invalida:
curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:8000/orders \ ─╯
-H 'Content-Type: application/json' \
-d '{"customerId":"cust-1"}'
{"error":"INVALID_ORDER","message":"customerId, address e items son obligatorios"}
Orden sin Stock
curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:8000/orders \ ─╯
-H 'Content-Type: application/json' \
-d '{
"customerId": "cust-1",
"address": "Calle Demo 1, Madrid",
"items": [{ "sku": "SKU-CAP", "qty": 999 }]
}'
{"id":"e30bb724-48ef-4e35-8625-e4085cf5bed2","customerId":"cust-1","address":"Calle Demo 1, Madrid","items":[{"sku":"SKU-CAP","qty":999}],"status":"REJECTED","reservationId":null,"notifications":[],"createdAt":"2026-09-07T22:40:55.847Z","error":{"error":"INSUFFICIENT_STOCK","message":"Stock insuficiente para SKU-CAP","sku":"SKU-CAP","requested":999,"available":5}}
HTTP 409
orden de producto inexistente:
curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:8000/orders \ ─╯
-H 'Content-Type: application/json' \
-d '{
"customerId": "cust-1",
"address": "Calle Demo 1, Madrid",
"items": [{ "sku": "SKU-NO-EXISTE", "qty": 1 }]
}'
{"id":"f2d77322-2733-4646-9054-d4efacf1f9dd","customerId":"cust-1","address":"Calle Demo 1, Madrid","items":[{"sku":"SKU-NO-EXISTE","qty":1}],"status":"REJECTED","reservationId":null,"notifications":[],"createdAt":"2026-09-07T22:44:20.307Z","error":{"error":"UNKNOWN_SKU","message":"El SKU SKU-NO-EXISTE no existe","sku":"SKU-NO-EXISTE"}}
HTTP 404
Finalmente os djeo aqui unos links para verificar si el problema estuviera en el API gateway:
curl -s http://localhost:8080/orders
curl -s http://localhost:8081/products
curl -s http://localhost:8082/v1/notifications
Bueno, hasta lo que os queria contar, más pruebas se podrian hacer tumbando alguno de los servicios, y tratando de hacer una orden, y ver lo que pasa, teneis el código disponible en github, aquí. Como en otras ocasiones espero que os haya parecido tan interesante como a mi.
Os veo pronto un saludo, Daniel.