Node.js Docker Arquitectura

Arquitectura de Microservicios con Node.js y Docker: Guía Práctica

Aprende a diseñar e implementar una arquitectura de microservicios con Node.js, Docker y Kubernetes. Patrones, errores comunes y cuándo realmente los necesitas.

Hans Vergara 10 min de lectura

Arquitectura de Microservicios con Node.js y Docker: Guía Práctica

Los microservicios son la arquitectura dominante en empresas tecnológicas. Pero también son la fuente del 70% del over-engineering en startups y empresas medianas. En esta guía, vamos directo al grano: cuándo usarlos, cómo implementarlos con Node.js y Docker, y cuándo un monolito es mejor.

¿Realmente necesitas microservicios?

Antes de cualquier línea de código, hazte estas preguntas:

Usa microservicios si:

  • Tu equipo tiene +5 desarrolladores trabajando en el mismo producto
  • Necesitas escalar componentes independientemente (ej: el servicio de pagos tiene 10x más carga que el de reportes)
  • Diferentes partes del sistema requieren tecnologías distintas
  • Necesitas deploys independientes sin afectar todo el sistema
  • Tu sistema tiene dominios claramente separados

Quédate con un monolito si:

  • Equipo de 1-4 personas
  • Estás en etapa de validación de producto
  • Tu carga es predecible y uniforme
  • La complejidad del dominio es manejable

Regla de Netflix: ellos popularizaron los microservicios, pero empezaron con un monolito. No copies la arquitectura de una empresa de 10.000 ingenieros cuando tienes 5.

Anatomía de una arquitectura de microservicios

  • API Gateway — punto de entrada único
    • Auth Service → PostgreSQL
    • Orders Service → MongoDB
    • Payment Service → PostgreSQL

Cada servicio tiene su propia base de datos y se comunica a través del gateway.

Componentes esenciales:

  1. API Gateway: punto de entrada único (Kong, Express Gateway, o custom)
  2. Service Discovery: cómo los servicios se encuentran entre sí
  3. Message Broker: comunicación asíncrona (RabbitMQ, Redis Streams, Kafka)
  4. Base de datos por servicio: cada microservicio tiene su propia BD
  5. Observabilidad: logs centralizados, métricas, tracing distribuido

Implementación paso a paso con Node.js

Estructura del proyecto

  • docker-compose.yml
  • gateway/ — API Gateway
    • Dockerfile, package.json
    • src/index.ts, routes.ts, middleware/
  • services/ — Microservicios
    • auth/ — Autenticación
    • orders/ — Pedidos
    • payments/ — Pagos
    • (cada uno con su Dockerfile, package.json, src/)
  • shared/types/ — Tipos compartidos

El Dockerfile estándar para Node.js

FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]

Puntos clave:

  • Multi-stage build: imagen final más liviana
  • USER node: nunca correr como root
  • Alpine: imagen base mínima (~50MB vs ~900MB de la full)

Docker Compose para desarrollo

version: '3.8'
services:
  gateway:
    build: ./gateway
    ports:
      - "3000:3000"
    environment:
      - AUTH_SERVICE_URL=http://auth:3001
      - ORDERS_SERVICE_URL=http://orders:3002
    depends_on:
      - auth
      - orders

  auth:
    build: ./services/auth
    ports:
      - "3001:3001"
    environment:
      - DATABASE_URL=postgresql://user:pass@auth-db:5432/auth
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      - auth-db

  orders:
    build: ./services/orders
    ports:
      - "3002:3002"
    environment:
      - DATABASE_URL=mongodb://orders-db:27017/orders
      - REDIS_URL=redis://redis:6379
    depends_on:
      - orders-db
      - redis

  auth-db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: auth
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass

  orders-db:
    image: mongo:7
    
  redis:
    image: redis:7-alpine

Patrones esenciales

1. API Gateway Pattern

El gateway es el punto de entrada único. Maneja:

  • Routing: dirigir requests al servicio correcto
  • Authentication: validar tokens antes de llegar al servicio
  • Rate limiting: proteger servicios de sobrecarga
  • Request aggregation: combinar respuestas de múltiples servicios

2. Event-Driven Communication

Los servicios no deberían llamarse directamente entre sí (esto crea acoplamiento). Usa eventos:

  1. Orders Service publica el evento order.created
  2. Message Broker distribuye a los consumidores:
    • Payment Service — procesa el pago
    • Notification Service — envía confirmación
    • Analytics Service — registra métricas

Esto tiene ventajas enormes:

  • Desacoplamiento: cada servicio funciona independientemente
  • Resiliencia: si un consumidor cae, el mensaje espera
  • Escalabilidad: puedes agregar consumidores sin modificar el productor

3. Circuit Breaker Pattern

Cuando un servicio externo falla, no quieres que las requests se acumulen:

// Usando opossum (circuit breaker para Node.js)
import CircuitBreaker from 'opossum';

const breaker = new CircuitBreaker(callExternalService, {
  timeout: 3000,      // máximo 3s de espera
  errorThreshold: 50,  // 50% de errores abre el circuito  
  resetTimeout: 10000  // reintenta cada 10s
});

breaker.fallback(() => cachedResponse);

4. Saga Pattern para transacciones distribuidas

Las transacciones ACID no existen entre microservicios. Usa Sagas:

1. Orders Service: Crear orden (estado: pending)
2. Payment Service: Procesar pago
   ├── Éxito → Orders: confirmar orden
   └── Fallo → Orders: cancelar orden (compensación)

Observabilidad: la clave que todos olvidan

Sin observabilidad, los microservicios son una pesadilla de debugging:

Los tres pilares:

  1. Logs estructurados: JSON, con correlation ID entre servicios
  2. Métricas: latencia, throughput, error rate por servicio (Prometheus + Grafana)
  3. Tracing distribuido: seguir un request a través de todos los servicios (OpenTelemetry)

Regla: si no puedes responder “¿por qué este request tardó 5 segundos?” en menos de 5 minutos, tu observabilidad es insuficiente.

Errores comunes

  1. Distributed monolith: microservicios que dependen sincrónicamente entre sí — lo peor de ambos mundos
  2. Sin ownership claro: cada servicio debe tener un equipo/persona responsable
  3. Shared database: si dos servicios comparten BD, no son microservicios
  4. Demasiado micro: un servicio para cada función — la granularidad excesiva mata la productividad
  5. Deploy manual: sin CI/CD automatizado, los microservicios son inmanejables

Stack recomendado para 2026

ComponenteRecomendación
RuntimeNode.js 22 + TypeScript
FrameworkFastify (más rápido que Express)
Message BrokerRedis Streams o RabbitMQ
API GatewayKong o custom con Fastify
ContainersDocker + Kubernetes (o Cloud Run)
CI/CDGitHub Actions
ObservabilidadOpenTelemetry + Grafana Stack
Base de datosPostgreSQL (default) + especializada según caso

Conclusión

Los microservicios son poderosos pero no son gratis. Requieren inversión en infraestructura, observabilidad y una cultura de ownership por equipo. Si los implementas bien, tu sistema será más resiliente, escalable y ágil. Si los implementas mal, tendrás un monolito distribuido con latencia extra.

En CloudLabs, diseñamos arquitecturas de microservicios que realmente tienen sentido para tu escala y equipo. No vendemos complejidad — vendemos la solución correcta. Hablemos de tu arquitectura.

¿Te interesa este tema?

En CloudLabs implementamos estas soluciones para empresas reales. Conversemos sobre tu proyecto.

Hablemos →
Hans Vergara

Hans Vergara

Lead Developer & Founder en CloudLabs