Los microservicios dividen una aplicación en servicios pequeños, desplegables de forma independiente, cada uno dueño de un dominio específico y que se comunican por la red. La naturaleza ligera y de arranque rápido de Node los favorece, pero la arquitectura introduce una complejidad de sistemas distribuidos que debes planificar.
Monolito vs microservicios
Monolito: una base de código/despliegue — simple de construir, más difícil de escalar equipos
Microservicios: muchos servicios pequeños — despliegue/escalado independiente, pero complejidad distribuida
Los microservicios cambian la simplicidad en proceso por flexibilidad operativa; solo vale la pena cuando realmente necesitas escalado/despliegue independiente o tienes múltiples equipos.
Comunicación entre servicios
Síncrona: REST / gRPC — solicitud/respuesta directa (simple, pero acoplamiento fuerte + fallos en cascada)
Asíncrona: colas de mensajes (RabbitMQ, Kafka) — eventos, desacoplado, resiliente
// el enfoque async/orientado a eventos desacopla los servicios — un servicio publica un evento;
// otros reaccionan sin que el publicador lo sepa o lo espere
await queue.publish("order.created", { orderId: 123 });
// el servicio de email, el servicio de inventario, etc. lo consumen de forma independiente
Prefiere la mensajería asíncrona para desacoplamiento y resiliencia donde sea posible; las llamadas síncronas son más simples pero crean acoplamiento fuerte y cascadas de fallos.
Aspectos clave que debes abordar
✓ Service discovery / API gateway — cómo los servicios se encuentran entre sí y enrutan el tráfico externo
✓ Propiedad de los datos — cada servicio es dueño de su PROPIA base de datos (sin BD compartida) → acoplamiento débil
✓ Resiliencia — timeouts, reintentos, circuit breakers (un servicio caído no debe causar cascada)
✓ Tracing distribuido y logging centralizado — depurar una solicitud a través de muchos servicios
✓ Consistencia eventual — sin transacciones entre servicios; usa sagas / eventos
✓ Autenticación — propaga la identidad (JWT) en las llamadas entre servicios
✓ Idempotencia — los mensajes reintentados no deben procesarse por duplicado
Ejemplo de resiliencia: circuit breaker
// deja de golpear una dependencia que falla; falla rápido y recupérate con elegancia
const breaker = new CircuitBreaker(callPaymentService, { timeout: 3000, errorThreshold: 50 });
breaker.fallback(() => ({ status: "queued" })); // degrada con elegancia cuando está caído
La honestidad sobre el trade-off
Los microservicios agregan complejidad real: fallos de red, depuración distribuida, consistencia
de datos, orquestación de despliegues, más infraestructura. Muchas aplicaciones funcionan mejor
como un monolito bien estructurado. Adopta microservicios por necesidades específicas, no por defecto.
Por qué es importante
Los microservicios son una decisión arquitectónica importante con trade-offs significativos.
Node los favorece (ligero, arranque rápido), pero las partes difíciles no son el código, sino las preocupaciones de sistemas distribuidos: la comunicación entre servicios (favoreciendo la mensajería async para desacoplar), la propiedad independiente de los datos, la resiliencia (timeouts/reintentos/circuit breakers para prevenir fallos en cascada), la observabilidad (tracing/logging entre servicios) y la consistencia eventual.
Comprender esto, y la realidad honesta de que los microservicios agregan una complejidad que muchas aplicaciones no necesitan (un buen monolito suele ser mejor), es lo que separa una decisión arquitectónica sólida de seguir una moda por inercia.
