Skip to content
StackPractices
advanced Por Mathias Paulenko

Arquitectura de Microservicios — Cuándo Usarla y Cuándo No

Guía práctica de microservicios: beneficios, trade-offs, patrones comunes y cuándo elegirlos sobre monolitos. Cubre estrategias de descomposición y complejidad operativa.

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Arquitectura de Microservicios — Cuándo Usarla y Cuándo No

Introducción

La arquitectura de microservicios estructura una aplicación como una colección de servicios débilmente acoplados, cada uno propiedad de un equipo pequeño y desplegable de forma independiente. Resuelve problemas de escalamiento organizacional y técnico, pero introduce complejidad operativa importante. Esta guía te ayuda a decidir cuándo el trade-off vale la pena.

¿Qué Son los Microservicios?

Un microservicio es una unidad de funcionalidad autónoma que:

  • Posee sus datos — cada servicio tiene su propia base de datos, sin esquemas compartidos
  • Es desplegable de forma independiente — un cambio en un servicio no requiere redeploy de otros
  • Se comunica por red — vía HTTP/gRPC o mensajería asíncrona
  • Es propiedad de un equipo pequeño — típicamente 5-15 ingenieros por servicio

Cuándo Usar Microservicios

SeñalPor Qué los Microservicios Ayudan
Múltiples equipos > 20 ingenierosReduce la sobrecarga de coordinación; equipos despliegan de forma independiente
Diferentes partes escalan de forma diferenteEscala solo el servicio caliente, no todo el monolito
Diferentes requerimientos tecnológicosUn servicio necesita GPU, otro alta I/O — usa la herramienta correcta
Necesidad de cadencia de release independienteLa API móvil se despliega diariamente, el servicio de facturación mensualmente
Aislamiento de fallos requeridoUn bug en búsqueda no debe caer pagos

Cuándo NO Usar Microservicios

SeñalAlternativa Mejor
< 10 ingenierosMonolito con límites de código modulares
Producto no probado / MVPMonolito — itera más rápido, divide después
Bajo tráfico / dominio simpleMonolito — más simple de operar
Sin cultura DevOps/SREMonolito — los microservicios requieren prácticas operativas maduras
Requerimientos de latencia estrictos (< 10ms)Monolito — los saltos de red agregan latencia

La Regla del Monolito Primero

Comienza con un monolito bien modularizado. Extrae servicios solo cuando un límite claro sea doloroso de mantener.

Tanto Martin Fowler como Sam Newman abogan por comenzar con un monolito y descomponer solo cuando el dolor es real. La descomposición prematura crea monolitos distribuidos — lo peor de ambos mundos.

Estrategias de Descomposición

1. Descomponer por Capacidad de Negocio

ServicioCapacidad de Negocio
Servicio de UsuariosGestión de cuentas, autenticación
Servicio de CatálogoListados de productos, búsqueda, inventario
Servicio de ÓrdenesCheckout, historial de órdenes, cumplimiento
Servicio de PagosCargos a tarjetas, reembolsos, facturación
Servicio de NotificacionesEmails, SMS, notificaciones push

Por qué funciona: los límites se alinean con cómo piensa y evoluciona el negocio.

2. Descomponer por Subdominio (DDD)

Usa contextos delimitados de Domain-Driven Design.

3. Patrón de la Higuera Estranguladora

Reemplaza gradualmente la funcionalidad del monolito con nuevos servicios.

Patrones de Comunicación

Síncrono (REST/gRPC)

Mejor para: consultas en tiempo real, request/response simple

Trade-off: Crea acoplamiento temporal. Si el servicio de Usuarios cae, el servicio de Catálogo se degrada.

Asíncrono (Event-driven)

Mejor para: trabajo en background, alto throughput, desacoplamiento

Trade-off: Consistencia eventual. Debugging es más difícil porque la ejecución no es lineal.

Propiedad de Datos y Consistencia

Base de Datos por Servicio

Nunca compartas una base de datos entre servicios. Crea acoplamiento oculto.

Manejando Necesidades de Datos Cruzados

  • Composición de API: consulta ambos servicios y une en el cliente
  • CQRS + Modelos de Lectura: desnormaliza datos vía eventos en un store local optimizado para lectura
  • Patrón Saga: coordina transacciones distribuidas usando transacciones compensatorias

Patrones Comunes

PatrónProblema que Resuelve
API GatewayPunto de entrada único, routing, auth, rate limiting
Service DiscoveryEncontrar instancias de servicios sin IPs hardcodeadas
Circuit BreakerFallar rápido cuando servicios downstream están unhealthy
BulkheadAislar thread pools para prevenir fallos en cascada
SagaManejar transacciones distribuidas entre servicios
CQRSSeparar modelos de lectura y escritura para performance

Lo que funciona

  • Posee el ciclo de vida completo — los equipos construyen, operan y soportan sus servicios
  • Diseña para el fallo — asume que cualquier dependencia puede fallar; usa reintentos con backoff, circuit breakers y degradación graceful
  • Automatiza todo — si un deploy o rollback requiere un runbook, automatízalo
  • Estandariza observabilidad — cada servicio debe emitir logs, métricas y trazas consistentes
  • Limita dependencias entre servicios — evita cadenas profundas; prefiere fan-out sobre árboles profundos

Errores Comunes

  • Crear demasiados servicios muy temprano — 5 servicios para 3 ingenieros es exceso
  • Compartir bases de datos entre servicios — esto es un monolito distribuido. Consulta diseño de bases de datos.
  • Ignorar latencia de red — cada llamada síncrona es un potencial timeout o retry storm
  • Subestimar costo operativo — los microservicios necesitan prácticas DevOps maduras
  • Construir un framework RPC custom — usa estándares probados (gRPC, HTTP/REST, o brokers de mensajes)

Ejemplo de Arquitectura

# docker-compose.yml — dos microservicios con API Gateway
services:
  api-gateway:
    image: nginx:alpine
    ports: ["8080:80"]
    depends_on: [user-service, order-service]

  user-service:
    build: ./services/user
    environment:
      DB_URL: postgres://db:5432/users
    depends_on: [db]

  order-service:
    build: ./services/order
    environment:
      DB_URL: postgres://db:5432/orders
      AMQP_URL: amqp://rabbitmq:5672
    depends_on: [db, rabbitmq]

  db:
    image: postgres:16
    volumes: ["pgdata:/var/lib/postgresql/data"]

  rabbitmq:
    image: rabbitmq:3-management
    ports: ["5672:5672", "15672:15672"]

volumes:
  pgdata:

Preguntas Frecuentes

¿Debería toda startup comenzar con microservicios?

No. Comienza con un monolito. Extrae servicios cuando un módulo sea doloroso de desplegar, escalar o razonar de forma independiente. La descomposición prematura es una causa común de ralentización de ingeniería.

¿Qué tan grande debería ser un microservicio?

Lo suficientemente pequeño para ser reescrito en 2-4 semanas. Si un servicio requiere 6+ ingenieros y meses para refactorizar, probablemente son múltiples servicios en disfraz. El “micro” se refiere al tamaño del equipo y alcance, no a líneas de código.

¿Cuál es el mayor riesgo de los microservicios?

Complejidad distribuida. Debuggear, probar y razonar sobre un sistema que abarca docenas de servicios es considerablemente más difícil que un monolito. Sin fuerte observabilidad y automatización, la arquitectura te ralentizará en lugar de acelerarte.

Temas Avanzados

Escenario Detallado: Descomposicion de Monolito de E-commerce

Sistema: E-commerce monolito (Rails, 500k lineas, 40 devs)
Problema: Deploy frequency semanal, lead time 5 dias, equipos bloqueados
Meta: 5 servicios extraidos en 12 meses

Analisis de dominios (DDD):
  Contextos delimitados identificados:
    1. Users (autenticacion, perfiles, direcciones)
    2. Catalog (productos, categorias, busqueda)
    3. Orders (checkout, historial, estado)
    4. Payments (tarjetas, reembolsos, facturacion)
    5. Notifications (email, SMS, push)
    6. Inventory (stock, reservas, reabastecimiento)

Matriz de extraccion (priorizar por riesgo bajo + valor alto):
  | Servicio | Riesgo | Valor | Esfuerzo | Prioridad |
  |----------|--------|-------|----------|-----------|
  | Notifications | Bajo | Medio | 4 sem | 1 (extraer primero) |
  | Inventory | Bajo | Alto | 6 sem | 2 |
  | Catalog | Medio | Alto | 8 sem | 3 |
  | Payments | Alto | Alto | 12 sem | 4 (parallel run) |
  | Orders | Alto | Critico | 16 sem | 5 (ultimo) |

Infraestructura compartida necesaria:
  - API Gateway: Kong o AWS API Gateway
    $ docker run -d --name kong -p 8000:8000 kong:latest
    $ curl -X POST http://localhost:8001/services \
        -d name=notification-service \
        -d url=http://notification-service:3000

  - Service Discovery: Consul o Kubernetes DNS
  - Tracing distribuido: Jaeger o AWS X-Ray
  - Centralized logging: ELK stack (Elasticsearch + Logstash + Kibana)
  - Metricas: Prometheus + Grafana
  - Mensajeria: RabbitMQ o Kafka para eventos async

Estrategia de datos por servicio:
  | Servicio | Base de datos | Justificacion |
  |----------|--------------|---------------|
  | Users | PostgreSQL | Datos relacionales, ACID necesario |
  | Catalog | MongoDB | Productos con atributos variables |
  | Orders | PostgreSQL | Transaccional, consistencia fuerte |
  | Payments | PostgreSQL | Transaccional, auditoria |
  | Notifications | DynamoDB | Key-value, alta escritura |
  | Inventory | PostgreSQL | Transaccional, reservas |

Comunicacion entre servicios:
  Sincrona (REST/gRPC):
    Orders -> Payments: cobrar al checkout
    Catalog -> Inventory: verificar stock

  Asincrona (eventos):
    Orders -> Notifications: "OrderPlaced" event
    Payments -> Orders: "PaymentConfirmed" event
    Inventory -> Catalog: "StockUpdated" event

  Configuracion de circuit breaker (Resilience4j):
    CircuitBreaker:
      failureRateThreshold: 50%
      waitDurationInOpenState: 30s
      slidingWindowSize: 10

Metricas despues de 12 meses:
  | Metrica | Monolito | 5 servicios |
  |---------|----------|-------------|
  | Deploy frequency | Semanal | Diario (por servicio) |
  | Lead time | 5 dias | 4 horas |
  | Tasa de fallo en deploy | 8% | 1.5% |
  | MTTR | 2 horas | 15 minutos |
  | Onboarding nuevo dev | 3 semanas | 1 semana |

Como manejo testing en microservicios?

Usa una estrategia de multiples niveles: unit tests dentro de cada servicio, contract tests entre servicios (Pact), integration tests con dependencias reales (Testcontainers), y E2E tests para flujos criticos. Los contract tests son los mas importantes en microservicios: verifican que el consumidor y el proveedor acuerdan el contrato. Sin contract tests, un cambio en un servicio rompe consumidores que no conoces.

Como manejo configuracion en microservicios?

Externaliza toda configuracion. Usa variables de entorno para valores que cambian por entorno (dev, staging, prod). Usa un servicio de configuracion centralizado (Spring Cloud Config, AWS AppConfig, Consul KV) para valores que cambian en runtime. Nunca hardcodees URLs de servicios, credenciales, o feature flags. Los secretos deben estar en un gestor (AWS Secrets Manager, HashiCorp Vault), no en archivos.

End of document. Review and update quarterly.