Microservicios
Cómo descomponer un sistema en servicios pequeños, autónomos y desplegables de forma independiente, alineados con dominios de negocio.
Qué problema resuelve
A medida que una aplicación monolítica crece, surgen problemas que frenan al equipo:
- Despliegues arriesgados: Un cambio pequeño requiere redesplegar toda la aplicación
- Escalado ineficiente: Para escalar una funcionalidad, debes escalar todo el monolito
- Equipos bloqueados: Múltiples equipos trabajan en el mismo código base y se bloquean mutuamente
- Tecnología homogénea: Todo el sistema debe usar el mismo lenguaje, framework y base de datos
- Tiempo de build creciente: Compilar y testear todo el monolito toma cada vez más tiempo
Monolito:
┌─────────────────────────────────┐
│ Aplicación │
│ ┌─────┐ ┌─────┐ ┌──────────┐ │
│ │Users│ │Prods│ │ Orders │ │
│ └─────┘ └─────┘ └──────────┘ │
│ ┌─────────────────────────────┐│
│ │ Base de datos única ││
│ └─────────────────────────────┘│
└─────────────────────────────────┘
Un deploy = todo el sistema
Un fallo = todo el sistema
Cómo funciona
La arquitectura de microservicios descompone el sistema en servicios pequeños e independientes, cada uno responsable de una capacidad de negocio específica. Cada servicio:
- Tiene su propia base de datos (Database per Service)
- Se despliega de forma independiente
- Se comunica con otros servicios a través de APIs o eventos
- Es propiedad de un equipo específico
Microservicios:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Users │ │ Products │ │ Orders │
│ Service │ │ Service │ │ Service │
│ ┌────┐ │ │ ┌────┐ │ │ ┌────┐ │
│ │ DB │ │ │ │ DB │ │ │ │ DB │ │
│ └────┘ │ │ └────┘ │ │ └────┘ │
└──────────┘ └──────────┘ └──────────┘
│ │ │
└──────── Event Bus ─────────┘
Principios de descomposición
La clave está en cómo dividir el sistema. Los criterios más efectivos son:
| Criterio | Descripción | Ejemplo |
|---|---|---|
| Por dominio de negocio | Cada servicio representa un bounded context de DDD | User Service, Order Service, Payment Service |
| Por capacidad de negocio | Cada servicio implementa una capacidad organizacional | Facturación, Inventario, Notificaciones |
| Por subdomain | Alineado con subdominios del negocio | Core (pedidos), Supporting (reportes), Generic (auth) |
Comunicación entre servicios
Los microservicios se comunican de dos formas principales:
Síncrona (request-response):
Order Service ──HTTP/gRPC──► Product Service
"¿Hay stock del producto X?"
← "Sí, 50 unidades"
Asíncrona (eventos):
Order Service ──evento──► Event Bus ──► Inventory Service
"OrderCreated" "Reservar stock"
──► Notification Service
"Enviar email"
Database per Service
Cada microservicio es dueño de sus datos. Ningún otro servicio accede directamente a su base de datos:
✅ Correcto:
Order Service ──API──► User Service
"Dame el nombre del usuario 123"
❌ Incorrecto:
Order Service ──SQL──► User DB
"SELECT name FROM users WHERE id=123"
Esto garantiza encapsulamiento y permite que cada servicio elija la tecnología de almacenamiento más adecuada (SQL, NoSQL, cache, etc.).
Ventajas
- Despliegue independiente: Cada servicio se despliega sin afectar a los demás
- Escalado granular: Escala solo los servicios que lo necesitan
- Autonomía de equipos: Cada equipo es dueño de su servicio end-to-end
- Diversidad tecnológica: Cada servicio puede usar el lenguaje y framework más adecuado
- Resiliencia: Un fallo en un servicio no necesariamente tumba todo el sistema
- Time to market: Equipos pequeños pueden entregar más rápido
Trade-offs / Desventajas
- Complejidad distribuida: Debugging, testing y tracing son significativamente más difíciles
- Consistencia eventual: Las transacciones distribuidas son complejas (requieren Sagas, Outbox)
- Latencia de red: La comunicación entre servicios añade latencia
- Overhead operativo: Más servicios = más despliegues, más monitoreo, más infraestructura
- Duplicación de datos: Cada servicio puede necesitar copias de datos de otros servicios
- Complejidad organizacional: Requiere equipos maduros con cultura DevOps
Cuándo usar
- Sistemas grandes con múltiples equipos de desarrollo
- Cuando diferentes partes del sistema tienen requisitos de escalado muy distintos
- Cuando necesitas desplegar funcionalidades de forma independiente y frecuente
- Organizaciones con equipos autónomos alineados con dominios de negocio
- Sistemas donde la resiliencia y la disponibilidad son críticas
Cuándo evitar
- Equipos pequeños (menos de 8-10 desarrolladores) donde el overhead no se justifica
- Sistemas con dominio simple que no requiere descomposición
- Cuando no tienes infraestructura de CI/CD, monitoreo y orquestación madura
- Prototipos o MVPs donde la velocidad de desarrollo es prioridad
- Cuando no puedes definir bounded contexts claros (señal de que el dominio no está bien entendido)
Tecnologías e implementaciones comunes
| Categoría | Opciones |
|---|---|
| Comunicación síncrona | REST, gRPC, GraphQL |
| Comunicación asíncrona | Kafka, RabbitMQ, Amazon SQS, NATS |
| Orquestación | Kubernetes, Docker Swarm, Amazon ECS |
| Service Discovery | Consul, Eureka, DNS-based (Kubernetes) |
| Observabilidad | Jaeger, Zipkin, OpenTelemetry, Prometheus |
| API Gateway | Kong, Traefik, AWS API Gateway |
Relación con otros patrones
- DDD (Domain-Driven Design): Proporciona los criterios para definir los límites de cada servicio
- API Gateway: Punto de entrada único para los clientes externos
- Saga Pattern: Maneja transacciones que cruzan múltiples servicios
- Event-Driven Architecture: Patrón de comunicación asíncrona entre servicios
- Circuit Breaker: Protege los servicios de fallos en cascada
Próximos pasos
Los microservicios son un estilo arquitectónico poderoso pero exigente. Para entender cómo manejar la comunicación asíncrona entre ellos, explora Event-Driven Architecture. Para proteger la comunicación con sistemas externos, revisa el Anti-Corruption Layer.