Saltar al contenido principal

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​

VentajaExplicación
Simplicidad de desarrolloUn solo proyecto, un solo IDE
Despliegue sencilloUn solo JAR/WAR para desplegar
Pruebas fácilesPruebas de integración sencillas
Transacciones ACIDUna sola BD = transacciones fáciles
Depuración sencillaStack trace completo
Rendimiento localSin latencia de red entre módulos
Refactorización fácilEl IDE puede renombrar en todo el proyecto

1.4 Desventajas del Monolito​

DesventajaExplicación
Escalado todo o nadaImposible escalar solo un módulo
Despliegue riesgosoUn bug = toda la app caída
Stack únicoTodo en Java (u otro)
Tiempo de build largoRecompilar todo cada vez
Conflictos de equipoMerges difíciles en un proyecto grande
Deuda técnicaDifícil de modernizar
Onboarding largoNuevo 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​

VentajaExplicación
Escalado dirigidoEscalar solo el servicio que lo necesita
Aislamiento de fallosUn servicio caído ≠ todo caído
Despliegue independienteDesplegar 10x por día por servicio
PolyglotJava, Node, Python, Go... según la necesidad
Equipos autónomos"You build it, you run it"
EscalabilidadAñadir un servicio sin tocar los demás
ModernizaciónReescribir un servicio a la vez

2.4 Desventajas de los Microservicios​

DesventajaExplicación
Complejidad distribuidaDepuración difícil
Latencia de redLlamadas HTTP entre servicios
Transacciones distribuidasACID imposible, Sagas necesarias
Consistencia eventualDatos no sincronizados inmediatamente
Operaciones complejasMonitoring, logging, tracing
Costo de infraestructuraMás servidores, más BDs
OverheadCada servicio = overhead de memoria

3. Comparación Detallada​

3.1 Tabla Comparativa​

CriterioMonolitoMicroservicios
Complejidad inicialBajaAlta
Complejidad a largo plazoAltaControlada
DespliegueTodo o nadaIndependiente
EscaladoVerticalHorizontal
TecnologíaÚnicaPolyglot
EquipoUn equipoEquipos por servicio
Base de datosCompartidaPor servicio
TransaccionesACID simpleSagas (complejo)
DepuraciónSencillaDistribuida (compleja)
LatenciaBajaMás alta
Costo inicialBajoAlto
Time to marketRápido al inicioMá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:

  1. El servicio arranca → se registra en Eureka
  2. El cliente quiere llamar al Service A
  3. El cliente pregunta a Eureka: "¿Dónde está el Service A?"
  4. Eureka devuelve la lista de instancias
  5. El cliente elige una instancia (load balancing client-side)
  6. 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​

#FactorDescripciónAplicación Spring Boot
1CodebaseUn repo = una appUn repo Git por servicio
2DependenciesDeclarar explícitamentepom.xml / build.gradle
3ConfigConfig en el entornoapplication.yml + env vars
4Backing ServicesTratar como recursos adjuntosDataSource configurable
5Build, Release, RunSeparar las etapasCI/CD pipeline
6ProcessesStatelessSesión en Redis
7Port BindingExportar vía puertoEmbedded Tomcat
8ConcurrencyEscalar vía procesosInstancias múltiples
9DisposabilityArranque/parada rápidaGraceful shutdown
10Dev/Prod ParityEntornos similaresDocker
11LogsTratar como flujosstdout → colector
12Admin ProcessesTareas admin one-offSpring 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​

  1. Identificar los Bounded Contexts en el monolito
  2. Comenzar por el menos riesgoso (ej: notificaciones)
  3. Extraer en un nuevo servicio
  4. Enrutar vía API Gateway
  5. Eliminar el código antiguo del monolito
  6. 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.


Capítulo Siguiente: Service Discovery con Eureka →

Volver al Módulo 13