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ñ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:
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.
Recursos Relacionados
System Design Interview Guide: Key Concepts
A practical guide to system design interviews: scalability, databases, caching, load balancing, microservices, and how to structure your answer.
GuideDomain-Driven Design (DDD) — A Practical Guide
Learn DDD fundamentals: bounded contexts, entities, value objects, aggregates, and how to model complex business domains in code.
GuideDocker for Developers — A Complete Guide
Learn Docker from the ground up: images, containers, Dockerfiles, networks, volumes, and Docker Compose for local development.
RecipeMicroservices Communication Patterns
Choose between synchronous and asynchronous communication patterns for resilient microservices architectures.
RecipeRetry with Exponential Backoff
Implement resilient retry strategies with exponential backoff, jitter, and circuit breaker integration for transient failure recovery.
RecipeService Discovery
Implement service discovery with health checks, DNS-based resolution, and service registries for live microservices environments.
RecipeWorkflow Engines
Orchestrate complex business processes with workflow engines, state machines, and long-running task coordination across distributed services.