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.
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ñal | Por Qué los Microservicios Ayudan |
|---|---|
| Múltiples equipos > 20 ingenieros | Reduce la sobrecarga de coordinación; equipos despliegan de forma independiente |
| Diferentes partes escalan de forma diferente | Escala solo el servicio caliente, no todo el monolito |
| Diferentes requerimientos tecnológicos | Un servicio necesita GPU, otro alta I/O — usa la herramienta correcta |
| Necesidad de cadencia de release independiente | La API móvil se despliega diariamente, el servicio de facturación mensualmente |
| Aislamiento de fallos requerido | Un bug en búsqueda no debe caer pagos |
Cuándo NO Usar Microservicios
| Señal | Alternativa Mejor |
|---|---|
| < 10 ingenieros | Monolito con límites de código modulares |
| Producto no probado / MVP | Monolito — itera más rápido, divide después |
| Bajo tráfico / dominio simple | Monolito — más simple de operar |
| Sin cultura DevOps/SRE | Monolito — 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
| Servicio | Capacidad de Negocio |
|---|---|
| Servicio de Usuarios | Gestión de cuentas, autenticación |
| Servicio de Catálogo | Listados de productos, búsqueda, inventario |
| Servicio de Órdenes | Checkout, historial de órdenes, cumplimiento |
| Servicio de Pagos | Cargos a tarjetas, reembolsos, facturación |
| Servicio de Notificaciones | Emails, 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ón | Problema que Resuelve |
|---|---|
| API Gateway | Punto de entrada único, routing, auth, rate limiting |
| Service Discovery | Encontrar instancias de servicios sin IPs hardcodeadas |
| Circuit Breaker | Fallar rápido cuando servicios downstream están unhealthy |
| Bulkhead | Aislar thread pools para prevenir fallos en cascada |
| Saga | Manejar transacciones distribuidas entre servicios |
| CQRS | Separar 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:
Notas de Producción
- Despliega gradualmente usando canary o blue-green para detectar regresiones temprano.
- Configura alertas para errores, latencia p99 y tasa de fallos antes de habilitar en producción.
- Documenta el rollback en el runbook; prueba el procedimiento en staging al menos una vez por trimestre.
- Revisa logs estructurados con correlation IDs para trazar requests end-to-end en incidentes.
Puntos Clave
- Aplica arquitectura de microservicios — cuándo usarla y cuándo no cuando necesites una solución práctica para tu caso de uso.
- Monitorea el rendimiento después de implementar; mide latencia, errores y uso de recursos antes y después.
- Revisa la sección de Troubleshooting ante errores comunes; la mayoría tienen causa raíz documentada con solución.
- Mantén dependencias actualizadas y ejecuta tests en CI para prevenir regresiones en producción.
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.
Troubleshooting
- High latency between services: trace the request path. Look for synchronous chains, missing caching, and oversized payloads that cross network boundaries.
- Single point of failure: identify components without redundancy. Add replicas, failover, or circuit breakers before scaling traffic.
- Unexpected coupling between services: review shared databases, libraries, and schemas. Bound contexts should own their data and expose stable interfaces.
- Cost spikes after scaling: Reserved capacity or spot instances can reduce steady-state spend.
- Difficult to reason about the system: maintain architecture decision records and service dependency maps.
Errores Comunes en Producción
- Tratar la guía como un checklist para completar una vez en lugar de una práctica por evolucionar.
- Adoptar cada recomendación de golpe en lugar de comenzar con un cambio medido.
- Saltar la evaluación de madurez e imponer prácticas avanzadas a un equipo no preparado.
- No actualizar runbooks y expectativas de guardia al introducir nuevas prácticas.
- Ignorar datos reales de incidentes al priorizar qué partes de la guía aplicar primero.
- No asignar un responsable que revise decisiones trimestralmente.
- Copiar ejemplos sin adaptarlos a las herramientas y restricciones reales del equipo.
- Olvidar medir resultados antes de agregar la siguiente mejora.
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.
Recursos Relacionados
Guía de Entrevistas de System Design: Conceptos Clave
Una guía práctica para entrevistas de system design: escalabilidad, bases de datos, caching, load balancing, microservicios y cómo estructurar tu respuesta.
GuideDomain-Driven Design (DDD): Guía Práctica
Aprende los fundamentos de DDD: bounded contexts, entidades, value objects, aggregates, y cómo modelar dominios de negocio complejos en código.
GuideDocker para Desarrolladores — Referencia Detallada
Aprende Docker desde cero: imágenes, contenedores, Dockerfiles, redes, volúmenes y Docker Compose para desarrollo local.
RecipePatrones de Comunicación entre Microservicios
Elige entre patrones de comunicación síncronos y asíncronos para arquitecturas de microservicios resilientes.
RecipeRetry con Exponential Backoff
Implementa estrategias de retry resilientes con exponential backoff, jitter e integración de circuit breaker para recuperación de fallas transitorias.
RecipeDescubrimiento de servicios
Implementa service discovery con health checks, resolución DNS-based y service registries para ambientes en vivo de microservicios.