Capítulo 1: Introducción a los Microservicios
🎯 Objetivos del Capítulo
- Comprender la diferencia entre monolito y microservicios
- Identificar las ventajas y desventajas de cada enfoque
- Descubrir los patrones esenciales de los microservicios
- Dominar los principios de la 12-Factor App
- Saber cuándo elegir qué arquitectura
1. Arquitectura Monolítica
1.1 Definición
Un monolito es una aplicación donde todas las funcionalidades se despliegan juntas en un solo proceso.
1.2 Ejemplo: E-Commerce Monolítico
// Structure typique d'un monolithe
src/main/java/com/ecommerce/
├── EcommerceApplication.java
├── config/
│ └── SecurityConfig.java
├── controller/
│ ├── UserController.java
│ ├── ProductController.java
│ ├── OrderController.java
│ ├── PaymentController.java
│ └── InventoryController.java
├── service/
│ ├── UserService.java
│ ├── ProductService.java
│ ├── OrderService.java
│ ├── PaymentService.java
│ └── InventoryService.java
├── repository/
│ ├── UserRepository.java
│ ├── ProductRepository.java
│ ├── OrderRepository.java
│ └── ...
└── model/
├── User.java
├── Product.java
├── Order.java
└── ...
1.3 Ventajas del Monolito
| Ventaja | Explicación |
|---|---|
| Simplicidad de desarrollo | Un solo proyecto, un solo IDE |
| Despliegue sencillo | Un solo JAR/WAR para desplegar |
| Pruebas fáciles | Pruebas de integración sencillas |
| Transacciones ACID | Una sola BD = transacciones fáciles |
| Depuración sencilla | Stack trace completo |
| Rendimiento local | Sin latencia de red entre módulos |
| Refactorización fácil | El IDE puede renombrar en todo el proyecto |
1.4 Desventajas del Monolito
| Desventaja | Explicación |
|---|---|
| Escalado todo o nada | Imposible escalar solo un módulo |
| Despliegue riesgoso | Un bug = toda la app caída |
| Stack único | Todo en Java (u otro) |
| Tiempo de build largo | Recompilar todo cada vez |
| Conflictos de equipo | Merges difíciles en un proyecto grande |
| Deuda técnica | Difícil de modernizar |
| Onboarding largo | Nuevo dev debe entender todo |
1.5 Cuándo el Monolito se Vuelve Problemático
Año 1: Startup
- 3 desarrolladores
- 50K líneas de código
- Despliegue: 5 minutos
→ Monolito PERFECTO ✅
Año 3: Crecimiento
- 30 desarrolladores
- 500K líneas de código
- Despliegue: 45 minutos
- Conflictos Git diarios
→ El monolito se vuelve un obstáculo ⚠️
Año 5: Escalado
- 100 desarrolladores
- 2M líneas de código
- Despliegue: 2 horas
- Black Friday: todo se cae
→ Monolito = "Big Ball of Mud" ❌
2. Arquitectura de Microservicios
2.1 Definición
Los microservicios son un enfoque donde la aplicación se divide en pequeños servicios autónomos, cada uno:
- Desplegable de manera independiente
- Con su propia base de datos
- Comunicándose vía API (HTTP/REST o mensajería)
- Gestionado por un pequeño equipo
2.2 Características Clave
1. Single Responsibility
Cada servicio hace UNA cosa bien:
- user-service: gestión de usuarios ÚNICAMENTE
- order-service: gestión de pedidos ÚNICAMENTE
- inventory-service: gestión de stock ÚNICAMENTE
2. Autonomía
Cada servicio:
- Tiene su propio repo Git
- Tiene su propia BD (Database per Service)
- Tiene su propio pipeline CI/CD
- Puede ser desplegado solo
- Puede usar una tecnología diferente
3. Comunicación
Sincrónica (HTTP/REST):
- El cliente espera la respuesta
- Sencillo pero acoplamiento
Asincrónica (Mensajería):
- Fire and forget
- Desacoplado pero complejo
2.3 Ventajas de los Microservicios
| Ventaja | Explicación |
|---|---|
| Escalado dirigido | Escalar solo el servicio que lo necesita |
| Aislamiento de fallos | Un servicio caído ≠ todo caído |
| Despliegue independiente | Desplegar 10x por día por servicio |
| Polyglot | Java, Node, Python, Go... según la necesidad |
| Equipos autónomos | "You build it, you run it" |
| Escalabilidad | Añadir un servicio sin tocar los demás |
| Modernización | Reescribir un servicio a la vez |
2.4 Desventajas de los Microservicios
| Desventaja | Explicación |
|---|---|
| Complejidad distribuida | Depuración difícil |
| Latencia de red | Llamadas HTTP entre servicios |
| Transacciones distribuidas | ACID imposible, Sagas necesarias |
| Consistencia eventual | Datos no sincronizados inmediatamente |
| Operaciones complejas | Monitoring, logging, tracing |
| Costo de infraestructura | Más servidores, más BDs |
| Overhead | Cada servicio = overhead de memoria |
3. Comparación Detallada
3.1 Tabla Comparativa
| Criterio | Monolito | Microservicios |
|---|---|---|
| Complejidad inicial | Baja | Alta |
| Complejidad a largo plazo | Alta | Controlada |
| Despliegue | Todo o nada | Independiente |
| Escalado | Vertical | Horizontal |
| Tecnología | Única | Polyglot |
| Equipo | Un equipo | Equipos por servicio |
| Base de datos | Compartida | Por servicio |
| Transacciones | ACID simple | Sagas (complejo) |
| Depuración | Sencilla | Distribuida (compleja) |
| Latencia | Baja | Más alta |
| Costo inicial | Bajo | Alto |
| Time to market | Rápido al inicio | Más lento al inicio |
3.2 Criterios de Decisión
4. Patrones Esenciales de Microservicios
4.1 API Gateway Pattern
Responsabilidades:
- Enrutamiento de solicitudes
- Autenticación/Autorización
- Rate Limiting
- Balanceo de carga
- Terminación SSL
- Caching
- Transformación de request/response
4.2 Service Discovery Pattern
Cómo funciona:
- El servicio arranca → se registra en Eureka
- El cliente quiere llamar al Service A
- El cliente pregunta a Eureka: "¿Dónde está el Service A?"
- Eureka devuelve la lista de instancias
- El cliente elige una instancia (load balancing client-side)
- El cliente llama directamente a la instancia
4.3 Circuit Breaker Pattern
Estados:
- CLOSED: Normal, las llamadas pasan
- OPEN: Demasiados fallos, llamadas bloqueadas → fallback
- HALF-OPEN: Prueba si el servicio ha vuelto
// Exemple avec Resilience4j
@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
public User getUser(Long id) {
return userServiceClient.getUser(id); // Appel distant
}
public User getUserFallback(Long id, Exception ex) {
return new User(id, "Unknown", "Service temporarily unavailable");
}
4.4 Saga Pattern (Transacciones Distribuidas)
En un monolito:
@Transactional
public void createOrder(Order order) {
orderRepository.save(order); // ✅
inventoryService.reserve(order); // ✅
paymentService.charge(order); // ❌ Échec → ROLLBACK tout
}
Con microservicios (Saga Choreography):
4.5 Event Sourcing & CQRS
Eventos en lugar de estados:
// Au lieu de: UPDATE orders SET status = 'SHIPPED'
// On stocke des événements:
OrderCreated { orderId: 1, items: [...], timestamp: ... }
PaymentReceived { orderId: 1, amount: 100, timestamp: ... }
OrderShipped { orderId: 1, trackingNumber: "ABC123", timestamp: ... }
5. The Twelve-Factor App
Los 12 factores son principios para construir aplicaciones cloud-native:
Los 12 Factores
| # | Factor | Descripción | Aplicación Spring Boot |
|---|---|---|---|
| 1 | Codebase | Un repo = una app | Un repo Git por servicio |
| 2 | Dependencies | Declarar explícitamente | pom.xml / build.gradle |
| 3 | Config | Config en el entorno | application.yml + env vars |
| 4 | Backing Services | Tratar como recursos adjuntos | DataSource configurable |
| 5 | Build, Release, Run | Separar las etapas | CI/CD pipeline |
| 6 | Processes | Stateless | Sesión en Redis |
| 7 | Port Binding | Exportar vía puerto | Embedded Tomcat |
| 8 | Concurrency | Escalar vía procesos | Instancias múltiples |
| 9 | Disposability | Arranque/parada rápida | Graceful shutdown |
| 10 | Dev/Prod Parity | Entornos similares | Docker |
| 11 | Logs | Tratar como flujos | stdout → colector |
| 12 | Admin Processes | Tareas admin one-off | Spring Batch |
Ejemplo: Factor 3 - Configuración
# ❌ Malo: Config hardcodeada
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: admin
password: secret123
# ✅ Bueno: Config externalizada
spring:
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USERNAME}
password: ${DATABASE_PASSWORD}
Ejemplo: Factor 6 - Procesos Stateless
// ❌ Malo: Estado en memoria
@RestController
public class CartController {
private Map<Long, Cart> carts = new HashMap<>(); // ¡Estado local!
@PostMapping("/cart/{userId}/add")
public void addItem(@PathVariable Long userId, @RequestBody Item item) {
carts.computeIfAbsent(userId, Cart::new).add(item);
}
}
// ✅ Bueno: Estado externalizado (Redis)
@RestController
public class CartController {
private final RedisTemplate<String, Cart> redis;
@PostMapping("/cart/{userId}/add")
public void addItem(@PathVariable Long userId, @RequestBody Item item) {
String key = "cart:" + userId;
Cart cart = redis.opsForValue().get(key);
if (cart == null) cart = new Cart();
cart.add(item);
redis.opsForValue().set(key, cart);
}
}
6. Domain-Driven Design (DDD) y Microservicios
6.1 Bounded Context
El Bounded Context define los límites de un microservicio:
6.2 Mismo Concepto, Contextos Diferentes
// En Sales Context
public class Customer {
private Long id;
private String name;
private String email;
private List<Order> orders;
private LoyaltyLevel loyaltyLevel;
}
// En Shipping Context
public class Customer {
private Long id;
private String name;
private Address shippingAddress;
private String phoneNumber;
}
// En Billing Context
public class Customer {
private Long id;
private String name;
private PaymentMethod defaultPaymentMethod;
private BillingAddress billingAddress;
}
7. Anti-Patrones a Evitar
7.1 Distributed Monolith
❌ ANTI-PATRÓN: Monolito distribuido
- 10 servicios que DEBEN ser desplegados juntos
- Esquema de BD compartido
- Acoplamiento fuerte entre servicios
- ¡Lo peor de ambos mundos!
7.2 Nano-Services
❌ ANTI-PATRÓN: Servicios demasiado pequeños
- Un servicio por entidad
- UserService, UserAddressService, UserPreferenceService
- Overhead enorme
- Transacciones imposibles
7.3 Shared Database
❌ ANTI-PATRÓN: Base de datos compartida
Service A ──┬──> PostgreSQL <──┬── Service B
│ │
└──> orders table <┘
Problema:
- Acoplamiento fuerte vía el esquema
- Imposible modificar una tabla
- Tentación de transacciones cruzadas
7.4 Sync Everywhere
❌ ANTI-PATRÓN: Todo en síncrono
Order Service
↓ HTTP sync
Inventory Service
↓ HTTP sync
Payment Service
↓ HTTP sync
Notification Service
Problema:
- Latencia acumulada
- Un servicio lento = todo lento
- Cascade failures
8. Migración Monolito → Microservicios
8.1 Estrategia: Strangler Fig Pattern
8.2 Pasos de Migración
- Identificar los Bounded Contexts en el monolito
- Comenzar por el menos riesgoso (ej: notificaciones)
- Extraer en un nuevo servicio
- Enrutar vía API Gateway
- Eliminar el código antiguo del monolito
- Repetir para el siguiente contexto
9. Resumen
Puntos Clave
✅ Monolito = Simple pero no escala
✅ Microservicios = Complejo pero flexible
✅ Comenzar monolito si es posible
✅ Migrar cuando el monolito se vuelve un obstáculo
✅ 12-Factor App = buenas prácticas cloud-native
✅ DDD = ayuda a definir los límites
✅ Patrones = Circuit Breaker, Saga, Event Sourcing
Checklist Antes de Microservicios
- ¿Equipo experimentado en DevOps?
- ¿Infraestructura Kubernetes/Docker?
- ¿Monitoring y logging centralizados?
- ¿CI/CD maduro?
- ¿Bounded Contexts bien definidos?
- ¿Problemas reales de escalado?
10. Quiz de Validación
Pregunta 1
¿Qué patrón permite evitar la cascada de fallos entre servicios?
Ver la respuesta
Circuit Breaker - Detecta los fallos y "abre" el circuito para devolver un fallback inmediatamente en lugar de esperar timeouts.
Pregunta 2
¿Por qué "Database per Service" es importante?
Ver la respuesta
- Desacoplamiento: Cada servicio puede evolucionar su esquema de forma independiente
- Autonomía: Sin dependencia de un DBA central
- Tecnología: PostgreSQL para un servicio, MongoDB para otro
- Evita el acoplamiento oculto vía el esquema compartido
Pregunta 3
¿Qué es un "Distributed Monolith"?
Ver la respuesta
Un anti-patrón donde hay varios servicios pero:
- Deben ser desplegados juntos
- Comparten una base de datos
- Están fuertemente acoplados
Se tiene la complejidad de los microservicios SIN las ventajas.