Todo lo que hemos visto hasta ahora ha sido conceptual: qué es un microservicio, qué se gana y qué se paga, cómo se compara con el monolito y cuándo conviene dar el paso. A partir de aquí, el curso se vuelve práctico, y para que lo práctico tenga sentido necesitamos un caso realista que nos acompañe de principio a fin. Ese caso es TechCorp y su tienda online, techcorp-shop.
En esta lección presentamos el hilo conductor con todo detalle: quién es TechCorp y quiénes son las personas que tomarán las decisiones, cómo es su monolito actual, qué problemas concretos sufre, cuál es el flujo de negocio central que usaremos una y otra vez ("un cliente hace un pedido"), un fragmento real del código actual que servirá de punto de partida, el mapa de servicios que construiremos, el stack tecnológico elegido y una hoja de ruta que explica qué avanzará cada módulo sobre este caso. No vamos a diseñar todavía los límites de los servicios (eso es trabajo del módulo 2) ni a implementar nada (módulo 4): aquí solo fijamos el escenario y las reglas del juego.
Contenido
- Quién es TechCorp
- La tienda hoy: un monolito Node.js/Express con una única PostgreSQL
- Las seis áreas funcionales
- Los problemas concretos que sufre TechCorp
- El flujo central: "un cliente hace un pedido"
- Un fragmento del monolito actual:
crearPedido - El mapa objetivo de servicios
- El stack tecnológico elegido y por qué
- Hoja de ruta del caso a lo largo del curso
- Errores comunes y consejos
- Ejercicios
- Conclusión
- Quién es TechCorp
TechCorp es una empresa ficticia de comercio electrónico que vende productos de electrónica de consumo (auriculares, altavoces, accesorios, pequeños dispositivos) a través de su tienda online techcorp-shop. Todos los datos que aparecerán en el curso (clientes, productos, importes, pedidos) son inventados.
Datos de contexto que iremos usando:
| Aspecto | Situación |
|---|---|
| Antigüedad de la tienda | Unos siete años en producción |
| Volumen habitual | Del orden de 3.000 pedidos diarios en un día normal |
| Picos | Black Friday y rebajas de enero: navegación por el catálogo multiplicada por 20, pedidos por 3-4 |
| Equipo técnico | Unas 25 personas: desarrollo, QA y sistemas |
| Organización actual | Un "equipo de back-end", un "equipo de front-end" y un "equipo de sistemas"; el back-end empieza a organizarse informalmente por áreas |
Dos personas aparecerán constantemente:
- Marta, CTO de TechCorp. Es quien decide la estrategia técnica, quien defiende (o no) las inversiones ante dirección y quien ha llegado a la conclusión, tras aplicar los criterios de la lección anterior, de que la tienda necesita evolucionar hacia microservicios de forma incremental. Marta pondrá las restricciones de negocio: no se puede parar la tienda, no hay presupuesto ilimitado, cada paso tiene que aportar valor.
- Luis, líder del equipo de Pedidos. Es el responsable del área más delicada de la tienda (donde se cruzan clientes, catálogo, stock, pagos y notificaciones) y será nuestro protagonista técnico: la mayor parte del código que escribiremos en el curso será el de
servicio-pedidos, y muchas decisiones las veremos desde su punto de vista.
- La tienda hoy: un monolito Node.js/Express con una única PostgreSQL
techcorp-shop es un único proyecto Node.js 20 con Express, escrito en JavaScript, que se ejecuta como un solo proceso (replicado en tres servidores idénticos detrás de un balanceador) y que utiliza una única base de datos PostgreSQL con todas las tablas de todas las áreas.
Estructura simplificada del repositorio actual:
techcorp-shop/ ├── servidor.js # arranca Express y registra todas las rutas ├── rutas/ │ ├── clientes.js │ ├── catalogo.js │ ├── inventario.js │ ├── pedidos.js │ ├── pagos.js │ └── notificaciones.js ├── controladores/ │ ├── clientesControlador.js │ ├── catalogoControlador.js │ ├── pedidosControlador.js # aquí vive crearPedido │ └── ... ├── bd/ │ ├── conexion.js # un único pool de PostgreSQL │ └── migraciones/ # todas las tablas juntas ├── servicios/ │ ├── correo.js # envío de emails │ └── pasarelaPago.js # llamada al proveedor de pagos └── package.json
Y el esquema de la base de datos, muy resumido:
-- Todas en la misma base de datos "techcorp"
CREATE TABLE clientes (id SERIAL PRIMARY KEY, email TEXT, nombre TEXT, direccion TEXT);
CREATE TABLE productos (id SERIAL PRIMARY KEY, nombre TEXT, precio NUMERIC(10,2), categoria TEXT, activo BOOLEAN);
CREATE TABLE stock (producto_id INT REFERENCES productos(id), cantidad INT, reservado INT);
CREATE TABLE pedidos (id SERIAL PRIMARY KEY, cliente_id INT REFERENCES clientes(id), estado TEXT, total NUMERIC(10,2), creado_en TIMESTAMP);
CREATE TABLE lineas_pedido (pedido_id INT REFERENCES pedidos(id), producto_id INT REFERENCES productos(id), cantidad INT, precio_unitario NUMERIC(10,2));
CREATE TABLE pagos (id SERIAL PRIMARY KEY, pedido_id INT REFERENCES pedidos(id), importe NUMERIC(10,2), estado TEXT, referencia_pasarela TEXT);
CREATE TABLE notificaciones (id SERIAL PRIMARY KEY, cliente_id INT, tipo TEXT, enviada_en TIMESTAMP);Fíjate en las claves foráneas cruzadas entre áreas (pedidos.cliente_id → clientes, lineas_pedido.producto_id → productos, pagos.pedido_id → pedidos). Hoy son una comodidad enorme; en el módulo 2 veremos que son también el principal obstáculo para separar los datos.
- Las seis áreas funcionales
El monolito agrupa seis áreas de negocio, que hoy son carpetas y tablas dentro del mismo proyecto y que mañana serán servicios:
| Área | Qué hace hoy | Tablas principales |
|---|---|---|
| Clientes | Registro, inicio de sesión, perfil, direcciones de envío. | clientes |
| Catálogo | Fichas de producto, categorías, precios, búsqueda. | productos |
| Inventario | Cantidades disponibles por producto, reservas al hacer pedido, entradas de almacén. | stock |
| Pedidos | Creación y ciclo de vida del pedido (pendiente, confirmado, cancelado, enviado). | pedidos, lineas_pedido |
| Pagos | Cobro a través de la pasarela externa, registro de pagos y devoluciones. | pagos |
| Notificaciones | Envío de correos y SMS al cliente (confirmación, envío, incidencias). | notificaciones |
Estas seis áreas se corresponderán, una a una, con los seis servicios del mapa objetivo. Que la correspondencia sea tan directa es una simplificación deliberada para el curso; en la vida real, encontrar los límites correctos es un trabajo en sí mismo, que abordaremos en el módulo 2.
- Los problemas concretos que sufre TechCorp
Marta no quiere microservicios por moda. Los quiere porque la tienda tiene problemas medibles que encajan con las señales de la lección 01-04:
-
Despliegues semanales con miedo. Toda la tienda se despliega los jueves por la noche. La suite de pruebas tarda 40 minutos; el despliegue, con comprobaciones manuales, dos horas. En el último trimestre, cuatro de doce despliegues se revirtieron total o parcialmente. Cualquier corrección urgente espera al jueves siguiente o requiere un despliegue de emergencia de toda la aplicación.
-
Caídas en campañas. En el último Black Friday, el tráfico de navegación por el catálogo saturó los tres servidores. Como el catálogo comparte proceso y base de datos con todo lo demás, la creación de pedidos y los cobros también se cayeron: la tienda no vendió durante 50 minutos en el día de más ventas del año. Para evitarlo, sistemas replicó todo el monolito a diez servidores durante la campaña, pagando por capacidad de pagos y clientes que no hacía falta.
-
Un equipo pisándose. Los desarrolladores de las distintas áreas trabajan sobre el mismo repositorio y las mismas tablas. Cuando el equipo de Notificaciones cambió una consulta sobre
pedidospara un nuevo correo, rompió un informe de Pedidos. Cuando Pedidos añadió una columna alineas_pedido, la migración falló en producción por un bloqueo con una consulta pesada de Catálogo. Los pull requests se atascan por conflictos, y el equipo de Luis calcula que dedica un 20 % del tiempo a coordinarse con otros equipos. -
Incidentes que se propagan. Un mes antes, un fallo del proveedor de correo hizo que el envío de emails empezara a acumular conexiones abiertas; la fuga de memoria terminó tumbando el proceso entero, incluidos los cobros.
-
Modelo de datos del catálogo forzado. Los productos tienen atributos muy variables (unos tienen talla, otros voltaje, otros compatibilidad) y en PostgreSQL se ha acabado con una tabla
atributos_productogenérica de clave-valor incómoda de consultar y de mantener.
Estos cinco problemas son el "porqué" de todo el curso. Cada módulo posterior resolverá alguno de ellos.
- El flujo central: "un cliente hace un pedido"
De todos los flujos de la tienda, uno los atraviesa todos: un cliente hace un pedido. Es el flujo que más valor tiene para el negocio, el que más áreas implica y el que más problemas concentra. Por eso será el ejemplo recurrente de todo el curso: lo modelaremos, lo dividiremos, lo implementaremos, lo desplegaremos, lo monitorizaremos y lo aseguraremos.
Los pasos, tal como ocurren hoy en el monolito:
- El cliente, ya identificado, envía su carrito (
clienteIdy una lista deproductoIdcon cantidades). - Se comprueba que el cliente existe y se recupera su dirección.
- Se consultan los productos para obtener nombre y precio actual.
- Se comprueba el stock de cada producto y se reserva (se incrementa
stock.reservado). - Se calcula el total y se crea el pedido en estado
PENDIENTEcon sus líneas. - Se cobra llamando a la pasarela de pago externa y se registra el pago.
- Si el cobro va bien, el pedido pasa a
CONFIRMADO, el stock reservado se descuenta definitivamente y se envía el correo de confirmación. - Si el cobro falla, se libera la reserva de stock, el pedido pasa a
CANCELADOy se envía un correo de incidencia.
Todo esto ocurre dentro de una única petición HTTP y, casi todo, dentro de una única transacción de base de datos.
sequenceDiagram
autonumber
actor C as Cliente
participant M as Monolito techcorp-shop
participant BD as PostgreSQL (única)
participant PP as Pasarela de pago (externa)
participant SMTP as Servidor de correo (externo)
C->>M: POST /pedidos {clienteId, lineas}
M->>BD: BEGIN
M->>BD: SELECT cliente
M->>BD: SELECT productos (precio)
M->>BD: UPDATE stock SET reservado = reservado + cantidad
M->>BD: INSERT pedido (PENDIENTE) + lineas
M->>PP: cobrar(total)
alt cobro correcto
PP-->>M: OK, referencia
M->>BD: INSERT pago; UPDATE pedido = CONFIRMADO; UPDATE stock (descontar)
M->>BD: COMMIT
M->>SMTP: enviar correo de confirmación
M-->>C: 201 Created {pedidoId, estado: CONFIRMADO}
else cobro rechazado
PP-->>M: rechazado
M->>BD: ROLLBACK (deshace reserva y pedido)
M->>SMTP: enviar correo de incidencia
M-->>C: 402 Payment Required
end
Cuando la tienda esté dividida en servicios, este mismo flujo se convertirá en una coreografía de eventos: servicio-pedidos creará el pedido y publicará pedido.creado; servicio-inventario reservará stock y publicará stock.reservado; servicio-pagos cobrará y publicará pago.confirmado; servicio-pedidos confirmará el pedido y publicará pedido.confirmado (o pedido.cancelado si algo falla); y servicio-notificaciones enviará el correo. Esa versión la diseñaremos en el módulo 2 y la construiremos en el 4; de momento, quédate con la versión monolítica y con los nombres de los eventos, que reaparecerán constantemente:
| Evento | Quién lo publica | Qué significa |
|---|---|---|
pedido.creado |
servicio-pedidos | Se ha registrado un pedido en estado PENDIENTE. |
stock.reservado |
servicio-inventario | El stock de las líneas del pedido ha quedado reservado. |
pago.confirmado |
servicio-pagos | La pasarela ha aceptado el cobro. |
pedido.confirmado |
servicio-pedidos | El pedido está completo: stock y pago correctos. |
pedido.cancelado |
servicio-pedidos | El pedido no ha podido completarse (sin stock, pago rechazado...). |
- Un fragmento del monolito actual:
crearPedido
crearPedidoEste es, simplificado pero fiel al espíritu del código real, el controlador Express que hoy implementa el flujo anterior. Léelo con calma: es el punto de partida de todo el curso, y volveremos sobre él para descomponerlo (módulo 2), extraer servicios (módulo 4) y compararlo con el resultado final (módulo 8).
// controladores/pedidosControlador.js (monolito techcorp-shop, versión actual)
const bd = require('../bd/conexion'); // único pool de PostgreSQL
const pasarelaPago = require('../servicios/pasarelaPago');
const correo = require('../servicios/correo');
async function crearPedido(req, res) {
const { clienteId, lineas } = req.body; // lineas: [{ productoId, cantidad }]
const cliente = await bd.conectar(); // una conexión para toda la operación
try {
await cliente.query('BEGIN');
// 1. Datos del cliente (tabla de OTRA área: clientes)
const { rows: [datosCliente] } = await cliente.query(
'SELECT id, email, nombre FROM clientes WHERE id = $1', [clienteId]);
if (!datosCliente) throw new Error('CLIENTE_NO_EXISTE');
// 2. Precios actuales (tabla de OTRA área: catálogo)
const ids = lineas.map(l => l.productoId);
const { rows: productos } = await cliente.query(
'SELECT id, nombre, precio FROM productos WHERE id = ANY($1) AND activo = true', [ids]);
if (productos.length !== ids.length) throw new Error('PRODUCTO_NO_DISPONIBLE');
// 3. Comprobar y reservar stock (tabla de OTRA área: inventario)
let total = 0;
for (const linea of lineas) {
const producto = productos.find(p => p.id === linea.productoId);
const { rowCount } = await cliente.query(
`UPDATE stock SET reservado = reservado + $1
WHERE producto_id = $2 AND cantidad - reservado >= $1`,
[linea.cantidad, linea.productoId]);
if (rowCount === 0) throw new Error('SIN_STOCK');
total += producto.precio * linea.cantidad;
}
// 4. Crear el pedido y sus líneas (tablas propias del área de pedidos)
const { rows: [pedido] } = await cliente.query(
`INSERT INTO pedidos (cliente_id, estado, total, creado_en)
VALUES ($1, 'PENDIENTE', $2, NOW()) RETURNING id`, [clienteId, total]);
for (const linea of lineas) {
const producto = productos.find(p => p.id === linea.productoId);
await cliente.query(
`INSERT INTO lineas_pedido (pedido_id, producto_id, cantidad, precio_unitario)
VALUES ($1, $2, $3, $4)`, [pedido.id, linea.productoId, linea.cantidad, producto.precio]);
}
// 5. Cobrar (llamada síncrona a un proveedor externo, en medio de la transacción)
const resultadoCobro = await pasarelaPago.cobrar({ importe: total, clienteId, pedidoId: pedido.id });
if (!resultadoCobro.ok) throw new Error('PAGO_RECHAZADO');
// 6. Registrar pago y confirmar (tabla de OTRA área: pagos)
await cliente.query(
`INSERT INTO pagos (pedido_id, importe, estado, referencia_pasarela)
VALUES ($1, $2, 'CONFIRMADO', $3)`, [pedido.id, total, resultadoCobro.referencia]);
await cliente.query(`UPDATE pedidos SET estado = 'CONFIRMADO' WHERE id = $1`, [pedido.id]);
for (const linea of lineas) {
await cliente.query(
`UPDATE stock SET cantidad = cantidad - $1, reservado = reservado - $1 WHERE producto_id = $2`,
[linea.cantidad, linea.productoId]);
}
await cliente.query('COMMIT');
// 7. Enviar email (área de notificaciones), fuera de la transacción pero en la misma petición
await correo.enviar({
para: datosCliente.email,
asunto: `Pedido ${pedido.id} confirmado`,
cuerpo: `Hola ${datosCliente.nombre}, tu pedido por ${total} € está confirmado.`
});
return res.status(201).json({ pedidoId: pedido.id, estado: 'CONFIRMADO', total });
} catch (error) {
await cliente.query('ROLLBACK');
const codigos = { CLIENTE_NO_EXISTE: 404, PRODUCTO_NO_DISPONIBLE: 400, SIN_STOCK: 409, PAGO_RECHAZADO: 402 };
return res.status(codigos[error.message] || 500).json({ error: error.message });
} finally {
cliente.liberar();
}
}
module.exports = { crearPedido };Qué observar en este código, porque cada punto será un tema del curso:
- Una función hace seis cosas de cinco áreas distintas: valida cliente, consulta catálogo, reserva inventario, crea pedido, cobra y notifica. Es fácil de leer, pero cualquier cambio en cualquier área pasa por aquí.
- Acceso directo a tablas de otras áreas (
clientes,productos,stock,pagos). Es lo que hace imposible que Notificaciones cambie su esquema sin avisar a Pedidos, y viceversa. La lección 02-04 tratará precisamente de romper esto. - Una transacción lo protege todo: si el pago falla, el
ROLLBACKdeshace la reserva de stock y el pedido. Es la comodidad que perderemos al separar bases de datos y que la lección 02-05 sustituirá con una saga. - Una llamada externa (la pasarela de pago) dentro de la transacción: mientras la pasarela responde (a veces segundos), las filas de
stockquedan bloqueadas para todos los demás pedidos. En campañas, esto contribuye a la saturación. - El envío de correo es síncrono: si el servidor de correo tarda o falla, la respuesta al cliente tarda o falla, aunque el pedido esté perfectamente cobrado. Esto es lo que las notificaciones asíncronas por eventos resolverán en el módulo 3.
- Sin idempotencia: si el cliente pulsa dos veces "comprar", se crean dos pedidos y se cobra dos veces. Hoy lo evita el front-end; en un sistema distribuido, no será suficiente.
- El mapa objetivo de servicios
Este es el sistema que iremos construyendo. Los nombres, puertos y bases de datos son fijos para todo el curso; los reutilizaremos en el código, en los ficheros de Docker Compose y en los manifiestos de Kubernetes.
| Servicio | Puerto | Responsabilidad | Base de datos |
|---|---|---|---|
servicio-clientes |
3004 | Registro, autenticación de clientes (delegada en Keycloak), perfil y direcciones. | PostgreSQL (clientes) |
servicio-catalogo |
3001 | Fichas de producto, categorías, precios, búsqueda. Alta lectura. | MongoDB (catalogo) |
servicio-inventario |
3006 | Stock disponible, reservas y liberaciones, entradas de almacén. | PostgreSQL (inventario) |
servicio-pedidos |
3002 | Creación y ciclo de vida del pedido; coordina el flujo mediante eventos. | PostgreSQL (pedidos) |
servicio-pagos |
3003 | Cobros contra la pasarela externa, registro de pagos y devoluciones. | PostgreSQL (pagos) |
servicio-notificaciones |
3005 | Envío de correos y SMS a partir de eventos de otros servicios. | Sin base de datos propia relevante (cola de RabbitMQ y registro mínimo de envíos) |
| API Gateway | 8080 | Punto de entrada único: enrutado, autenticación JWT, límites de uso. | — |
Y la infraestructura de apoyo que rodeará a los servicios:
| Pieza | Papel |
|---|---|
| RabbitMQ | Broker de mensajes para los eventos pedido.creado, stock.reservado, pago.confirmado, pedido.confirmado, pedido.cancelado. |
| Keycloak | Servidor de identidad (OAuth2 / OpenID Connect) que emite los JWT que el gateway y los servicios validan. |
| Prometheus, Grafana, Loki | Métricas, paneles y logs centralizados. |
| OpenTelemetry + Jaeger | Trazas distribuidas para seguir un pedido a través de todos los servicios. |
| Docker / Docker Compose | Empaquetado y entorno local de desarrollo. |
| Kubernetes (+ Istio) | Orquestación en producción y malla de servicios. |
| GitHub Actions | Pipelines de integración y despliegue continuo. |
flowchart TB
Cliente[Navegador / App móvil] --> GW[API Gateway :8080]
KC[Keycloak] -. JWT .-> GW
GW --> CLI[servicio-clientes :3004]
GW --> CAT[servicio-catalogo :3001]
GW --> PED[servicio-pedidos :3002]
CLI --> BD1[(PG clientes)]
CAT --> BD2[(MongoDB)]
PED --> BD3[(PG pedidos)]
INV[servicio-inventario :3006] --> BD4[(PG inventario)]
PAG[servicio-pagos :3003] --> BD5[(PG pagos)]
NOT[servicio-notificaciones :3005]
PED <-. eventos .-> MQ[(RabbitMQ)]
MQ <-. eventos .-> INV
MQ <-. eventos .-> PAG
MQ -. eventos .-> NOT
PAG --> PP[Pasarela de pago externa]
NOT --> SMTP[Correo / SMS externo]
- El stack tecnológico elegido y por qué
Marta y los líderes de equipo han fijado el stack con dos criterios: continuidad con lo que el equipo ya conoce (para no aprender un lenguaje nuevo a la vez que una arquitectura nueva) y estándares de mercado para el resto de piezas.
| Decisión | Elección | Por qué |
|---|---|---|
| Lenguaje y framework de los servicios | Node.js 20 + Express (JavaScript) | Es lo que ya usa el monolito; el equipo lo domina. Los microservicios permitirían otros lenguajes, pero TechCorp no tiene hoy una razón para ello. Identificadores en español (crearPedido, PedidoRepositorio, clienteId) por coherencia con el código existente. |
| Base de datos relacional | PostgreSQL (una por servicio) | Ya se conoce y se opera; encaja con pedidos, pagos, inventario y clientes, donde importan las transacciones locales. |
| Base de datos del catálogo | MongoDB | Resuelve el problema real de los atributos variables de producto. Es el único caso de heterogeneidad justificada. |
| Mensajería | RabbitMQ | Broker maduro, sencillo de operar a la escala de TechCorp, con soporte de confirmaciones y colas de errores. |
| Empaquetado y entorno local | Docker y Docker Compose | Estándar de facto; permite levantar toda la tienda en un portátil. |
| Orquestación | Kubernetes | Estándar de facto para operar contenedores en producción; el equipo de sistemas ya lo ha probado. |
| CI/CD | GitHub Actions | El código ya está en GitHub; se evita otra herramienta. |
| Observabilidad | Prometheus, Grafana, Loki, OpenTelemetry, Jaeger | Conjunto de código abierto ampliamente usado que cubre métricas, paneles, logs y trazas. |
| Seguridad | JWT/OAuth2 con Keycloak | Identidad centralizada y estándar; los servicios solo validan tokens. |
| Malla de servicios | Istio | Cifrado entre servicios, políticas y observabilidad de red sin tocar el código de los servicios. Se introduce al final, cuando el resto ya funciona. |
Una nota importante para tu aprendizaje: estas elecciones son razonables, no las únicas. En la lección 04-01 discutiremos alternativas (Kafka frente a RabbitMQ, otros lenguajes, otros orquestadores). Aquí simplemente fijamos el terreno para que todos los ejemplos del curso sean coherentes.
- Hoja de ruta del caso a lo largo del curso
Así avanzará la historia de TechCorp módulo a módulo. Cada fila responde a la pregunta "¿qué le pasa a la tienda en este módulo?".
| Módulo | Qué avanza el caso de TechCorp | Problema de TechCorp que ataca |
|---|---|---|
| 1. Introducción (este) | Se fija el escenario, el flujo central y el mapa objetivo. Marta decide migrar de forma incremental. | Entender el porqué. |
| 2. Diseño | Se analizan las seis áreas, se definen sus límites (bounded contexts), se decide qué datos son de quién y cómo se sustituye la gran transacción de crearPedido por una saga basada en eventos. |
Equipos que se pisan; transacción única. |
| 3. Comunicación | Se diseñan las APIs REST de cada servicio, los eventos de RabbitMQ del flujo de pedido, el gateway en el 8080, cómo se encuentran los servicios y cómo se versionan los contratos. | Acoplamiento entre áreas; correo síncrono. |
| 4. Implementación | Se construyen de verdad los servicios en Node.js/Express, empezando por servicio-pedidos con Luis: configuración, consumo de APIs, publicación de eventos y pruebas (unitarias, de integración, de contrato). |
Punto de partida real del código. |
| 5. Despliegue | Se empaquetan los servicios en Docker, se levanta la tienda con Docker Compose y luego en Kubernetes, se montan pipelines en GitHub Actions y se despliega servicio-catalogo en canary. Se añade Istio. |
Despliegues semanales con miedo. |
| 6. Monitoreo | Se instrumentan métricas, logs y trazas del flujo de pedido; se diseñan reintentos, circuit breakers y colas de errores; se escala el catálogo para el Black Friday; se definen SLOs y alertas. | Caídas en campañas; incidentes que se propagan. |
| 7. Seguridad | Se protege el gateway con JWT de Keycloak, se cifra la comunicación entre servicios y se endurecen contenedores y clúster. | Superficie de ataque de un sistema distribuido. |
| 8. Casos de estudio | Se recorre la migración completa de principio a fin (patrón strangler), se ve la implementación y el despliegue completos del sistema, y se extraen lecciones. | Consolidar todo. |
flowchart LR
M1[M1<br/>Escenario] --> M2[M2<br/>Diseño de límites<br/>y datos] --> M3[M3<br/>APIs, eventos<br/>y gateway] --> M4[M4<br/>Código de los<br/>servicios]
M4 --> M5[M5<br/>Docker, K8s,<br/>CI/CD] --> M6[M6<br/>Observabilidad<br/>y resiliencia] --> M7[M7<br/>Seguridad] --> M8[M8<br/>Migración completa<br/>y lecciones]
Un detalle que conviene tener presente desde ya: el curso no hace un "big bang". TechCorp no apagará el monolito un viernes para encender seis servicios el lunes. Los servicios se extraerán uno a uno, empezando por el catálogo (la razón de escalado más clara y el menor riesgo), después el flujo de pedidos, y el monolito irá adelgazando hasta desaparecer. Ese proceso incremental se detalla en la lección 08-01.
Errores Comunes y Consejos
- Perder de vista el porqué. Cuando en el módulo 5 estés peleándote con un manifiesto de Kubernetes, recuerda los cinco problemas del apartado 4: cada pieza de infraestructura está ahí para resolver uno de ellos, no por sí misma.
- Idealizar el punto de llegada. El mapa de servicios del apartado 7 es el objetivo, pero se construirá paso a paso, y algunas decisiones se revisarán por el camino (es lo normal en un proyecto real).
- Despreciar el código del monolito.
crearPedidoes un código razonable para un monolito; no es "malo", es inadecuado para el problema que TechCorp tiene ahora. Entenderlo bien es la clave para descomponerlo bien. - Cambiar los nombres. Mantén los nombres de servicios, puertos y eventos exactamente como aparecen en las tablas de esta lección; el resto del curso los da por supuestos.
- Consejo: guarda una copia mental (o real) del diagrama de secuencia del apartado 5. Lo compararás con su versión en eventos en el módulo 2 y con la traza distribuida real en el módulo 6, y esa comparación es una de las mejores formas de entender qué cambia con los microservicios.
Ejercicios
Ejercicio 1: Trazar el flujo
Sin mirar el diagrama de secuencia, escribe en orden los ocho pasos del flujo "un cliente hace un pedido" tal como ocurre hoy en el monolito, e indica para cada paso a qué área funcional pertenece.
Ejercicio 2: Leer crearPedido con ojos de arquitecto
Sobre el fragmento del apartado 6, responde:
- ¿Qué tablas consulta o modifica que no pertenecen al área de pedidos?
- ¿Qué ocurre si la pasarela de pago tarda 15 segundos en responder? ¿A quién afecta?
- ¿Qué ocurre si el envío del correo falla después del
COMMIT? ¿Qué ve el cliente y qué queda en la base de datos?
Ejercicio 3: Relacionar problemas y módulos
Para cada uno de los cinco problemas del apartado 4, indica qué módulo (o módulos) del curso lo abordarán y con qué pieza del mapa objetivo o del stack se relaciona.
Soluciones
Ejercicio 1
- El cliente envía el carrito → Pedidos (punto de entrada).
- Comprobar cliente y recuperar dirección → Clientes.
- Consultar productos y precios → Catálogo.
- Comprobar y reservar stock → Inventario.
- Calcular total y crear pedido
PENDIENTE→ Pedidos. - Cobrar en la pasarela y registrar el pago → Pagos.
- Confirmar pedido, descontar stock y enviar correo → Pedidos, Inventario y Notificaciones.
- Si el cobro falla: liberar reserva, cancelar y notificar incidencia → Inventario, Pedidos y Notificaciones.
Ejercicio 2
clientes(área de clientes),productos(catálogo),stock(inventario) ypagos(pagos). Solopedidosylineas_pedidoson propias.- La transacción sigue abierta durante esos 15 segundos, con las filas de
stockde esos productos bloqueadas por elUPDATE. Cualquier otro pedido que incluya alguno de esos productos se queda esperando. En una campaña, muchos pedidos esperando bloqueos agotan las conexiones y el sistema entero se degrada. Además, el cliente ve la página "cargando" durante 15 segundos. - El pedido está confirmado, cobrado y con stock descontado en la base de datos (el
COMMITya se ha ejecutado). Perocorreo.enviarlanza una excepción, elcatchejecuta unROLLBACK(que ya no deshace nada, porque la transacción terminó) y responde al cliente con un 500. El cliente cree que su compra ha fallado, aunque se le ha cobrado; probablemente vuelva a intentarlo y se le cobre dos veces. Es un ejemplo perfecto de por qué las notificaciones deben ser asíncronas y desacopladas.
Ejercicio 3
| Problema | Módulos | Piezas relacionadas |
|---|---|---|
| Despliegues semanales con miedo | 5 (Docker, Kubernetes, CI/CD, estrategias de despliegue) y 4 (pruebas por servicio) | GitHub Actions, contenedores, despliegue canary |
| Caídas en campañas | 6 (escalabilidad, resiliencia) y 2/3 (catálogo como servicio aparte) | servicio-catalogo escalado independientemente, Prometheus/Grafana |
| Un equipo pisándose | 2 (límites y una base de datos por servicio) y 3 (contratos) | Bounded contexts, APIs versionadas |
| Incidentes que se propagan | 3 (mensajería asíncrona) y 6 (gestión de errores, circuit breakers) | RabbitMQ, servicio-notificaciones aislado |
| Modelo de datos del catálogo forzado | 2 (una base de datos por servicio) y 4 (implementación) | MongoDB en servicio-catalogo |
Conclusión
En esta lección hemos presentado a fondo el caso que vertebra el curso: TechCorp, una tienda online que hoy es un monolito Node.js/Express sobre una única PostgreSQL con seis áreas funcionales, y que sufre problemas concretos y medibles: despliegues semanales temidos, caídas en campañas por saturación del catálogo, equipos que se pisan sobre el mismo código y las mismas tablas, incidentes que se propagan de áreas secundarias a las críticas y un modelo de datos forzado en el catálogo. Hemos recorrido paso a paso el flujo central "un cliente hace un pedido" y hemos leído el código real de crearPedido, que concentra en una sola función y una sola transacción el trabajo de cinco áreas: ese fragmento es nuestro punto de partida. Hemos fijado el mapa objetivo (seis servicios con sus puertos y bases de datos más un gateway), el stack tecnológico y sus razones, los cinco eventos del flujo de pedido y una hoja de ruta módulo a módulo.
Con el escenario definido, el módulo 2 empieza el trabajo de verdad: diseñar los microservicios. Partiremos de los principios de diseño, aprenderemos a descomponer el monolito de TechCorp, definiremos sus bounded contexts, decidiremos cómo repartir los datos entre servicios y sustituiremos la gran transacción de crearPedido por una saga coordinada mediante eventos. Marta ha tomado la decisión; a partir de ahora acompañamos a Luis y a su equipo en la ejecución.
Curso de Microservicios
Módulo 1: Introducción a los Microservicios
- Conceptos Básicos de Microservicios
- Ventajas y Desventajas de los Microservicios
- Comparación con la Arquitectura Monolítica
- Cuándo Adoptar Microservicios: Criterios de Decisión
- El Caso Práctico del Curso: la Tienda Online de TechCorp
Módulo 2: Diseño de Microservicios
- Principios de Diseño de Microservicios
- Descomposición de Aplicaciones Monolíticas
- Definición de Bounded Contexts
- Gestión de Datos: una Base de Datos por Servicio
- Consistencia Distribuida: Sagas, CQRS y Event Sourcing
Módulo 3: Comunicación entre Microservicios
- APIs RESTful
- Mensajería Asíncrona
- Protocolos de Comunicación: gRPC, GraphQL
- API Gateway y Backend for Frontend
- Descubrimiento de Servicios y Balanceo de Carga
- Contratos y Versionado de APIs
Módulo 4: Implementación de Microservicios
- Elección de Tecnologías y Herramientas
- Desarrollo de un Microservicio Simple
- Gestión de Configuración
- Integración Práctica: Consumir APIs y Publicar Eventos
- Pruebas en Microservicios: Unitarias, de Integración y de Contrato
Módulo 5: Despliegue y Orquestación
- Contenedores y Docker
- Orquestación con Kubernetes
- CI/CD para Microservicios
- Estrategias de Despliegue: Rolling, Blue-Green y Canary
- Service Mesh: Istio y Linkerd
Módulo 6: Monitoreo y Mantenimiento
- Monitoreo y Logging
- Trazabilidad Distribuida con OpenTelemetry
- Gestión de Errores y Recuperación
- Escalabilidad y Rendimiento
- SLOs, Alertas y Gestión de Incidentes
Módulo 7: Seguridad en Microservicios
- Autenticación y Autorización
- Seguridad en la Comunicación
- Prácticas de Seguridad
- Seguridad en Contenedores y Kubernetes
