Durante siete módulos hemos construido piezas: límites y datos (módulo 2), contratos y eventos (3), código (4), despliegue (5), operación (6) y seguridad (7). En cada una aparecía TechCorp como ejemplo, pero nunca hemos contado la historia entera en orden: qué hizo el equipo primero, qué después, qué se torció por el camino y con qué resultado. Esta lección es ese relato, el que el cierre del módulo 7 anunciaba: cómo se ejecutó la migración paso a paso. Es un caso de estudio, no una lección de técnica: cuando una fase use el strangler fig, la outbox o el canary, remitiremos a la lección donde se explica en lugar de repetirla, y no escribiremos código (los servicios que faltan se implementan en 08-02 y el sistema completo se despliega en 08-03).
Recorreremos los ~13 meses ficticios de la migración (julio de 2025 a agosto de 2026): la fase 0 de deberes previos, las seis fases de extracción en el orden decidido en 02-02 (catálogo, inventario, notificaciones, pagos, pedidos, clientes), el apagado del monolito, una tabla cronológica, las métricas antes y después, los costes reales y tres tropiezos que conviene conocer para no repetirlos. Todas las fechas, cifras y nombres son inventados, pero verosímiles para una tienda de 3.000 pedidos diarios y 25 técnicos.
Contenido
- Punto de partida y plan (julio de 2025)
- Fase 0: los deberes previos (julio–septiembre de 2025)
- Fase 1: catálogo, la primera extracción y el primer Black Friday (septiembre–noviembre de 2025)
- Fase 2: inventario, el primer consumidor de eventos (diciembre de 2025–enero de 2026)
- Fase 3: notificaciones, el correo fuera del proceso (febrero de 2026)
- Fase 4: pagos, dinero y cautela (marzo–abril de 2026)
- Fase 5: pedidos, la saga sustituye a la transacción (mayo–julio de 2026)
- Fase 6: clientes y el apagado del monolito (julio–agosto de 2026)
- Tabla cronológica de la migración
- Métricas antes y después
- Costes reales y lo que salió mal
- La arquitectura final
- Punto de partida y plan (julio de 2025)
En julio de 2025 Marta presenta a dirección el plan que salió del análisis de 01-04 y 01-05: techcorp-shop es un monolito Node.js/Express sobre una única PostgreSQL con cinco problemas medibles (despliegues de jueves con 4 de 12 reversiones, 50 minutos sin vender en el Black Friday anterior, un 20 % del tiempo en coordinación, la fuga de memoria del correo que tumbó los cobros y la tabla atributos_producto). La propuesta no es "reescribir la tienda", sino extraer seis servicios uno a uno, con la tienda vendiendo, en el orden de 02-02: catálogo → inventario → notificaciones → pagos → pedidos → clientes.
Tres condiciones que Marta pone y que gobiernan todo lo que sigue:
| Condición | Consecuencia práctica |
|---|---|
| La tienda no se para. Ningún corte planificado de más de unos minutos; ninguna campaña en riesgo. | Todo se hace con strangler fig y branch by abstraction (02-02) y con vuelta atrás por configuración. |
| Cada fase aporta valor visible en menos de un trimestre. | El catálogo va primero porque resuelve el problema más caro (las caídas de campaña) con el menor riesgo. |
| Máximo dos tecnologías de base de datos y un solo lenguaje. | PostgreSQL + MongoDB (02-04); Node.js 20 en todos los servicios (04-01). |
Y una regla de Luis que aparecerá en cada fase: "si se hace más de una vez por servicio, se automatiza antes del segundo" (04-01, 05-03). Es la que convierte una migración de seis servicios en algo que un equipo de 25 personas puede sostener.
- Fase 0: los deberes previos (julio–septiembre de 2025)
La primera decisión importante fue no extraer nada durante tres meses. Los cuatro prerrequisitos de 01-04 (CI/CD, contenedores, observabilidad, cultura de propiedad) no existían, y extraer un servicio sin ellos habría producido un monolito distribuido operado a mano. Lo que se hizo:
- Organización. Los equipos "back-end / front-end / sistemas" se reorganizaron en los cuatro equipos de 02-01: Experiencia de compra (catálogo y clientes), Pedidos (pedidos e inventario, con Luis), Pagos y comunicaciones (pagos y notificaciones) y Plataforma (gateway, Kubernetes, CI/CD, observabilidad). Cada equipo, dueño de sus áreas dentro del monolito desde el primer día: la ley de Conway aplicada antes de tocar el código.
- CI para el monolito. El
ci.ymlde 05-03 en su versión mínima: pruebas en cada PR, imagen Docker del monolito (Dockerfilemulti-stage de 05-01) publicada enghcr.io/techcorp/techcorp-shop:sha-…. La suite de 40 minutos se paralelizó a 12. Todavía se desplegaba los jueves, pero con una imagen reproducible. - Kubernetes y el gateway. Plataforma levantó el clúster (05-02), desplegó el monolito en él (tres réplicas, sondas de 03-05) y puso el gateway del puerto 8080 delante con todas las rutas apuntando al monolito (03-04). Durante seis semanas el gateway no hizo nada más que reenviar y registrar: es el requisito del strangler fig de 02-02, "la fachada existe antes de la primera extracción".
- Observabilidad mínima. Logs estructurados con pino enviados a Loki, métricas RED del gateway y del monolito en Prometheus, un panel en Grafana (06-01). Sin trazas aún.
- El monolito modular. La matriz tabla × área de 02-02 había destapado que
crearPedidoescribía enstocky enpagossin ser su dueño. Antes de mover nada, el equipo de Luis creó dentro del monolito los módulosalmacen.reservar()/almacen.liberar()ypagos.registrarCobro(), ycrearPedidopasó a invocarlos en lugar de escribir SQL ajeno. Fue el trabajo menos vistoso de la migración y el que más la aceleró: cuando llegaron las fases 2 y 4, la costura ya estaba hecha. @techcorp/comun-httpv0.1 (04-01): logger,X-Request-Id, errores RFC 7807, rutas de salud. Se adoptó primero en el monolito, para que el primer servicio naciera con la misma cara.
Resultado de la fase 0 (30 de septiembre de 2025): ningún servicio extraído, ningún problema de negocio resuelto todavía… y una plataforma sobre la que las seis fases siguientes se apoyarían sin volver a discutir herramientas.
- Fase 1: catálogo, la primera extracción y el primer Black Friday (septiembre–noviembre de 2025)
servicio-catalogo (3001, MongoDB) se construyó tal como se ve en 04-02, con la plantilla plantilla-servicio-node recién creada. La secuencia fue la de 02-04 §6, y merece verse con fechas porque es el molde de todas las demás:
| Semana | Paso | Técnica (lección) |
|---|---|---|
| 1–3 (sep) | Servicio con GET /v1/productos en sus tres formas y contrato OpenAPI. Pruebas de componente y contrato. |
04-02, 04-05 |
| 4 | Carga inicial con scripts/migrar-catalogo.js (idempotente, upsert): 41.000 productos y sus atributos clave-valor convertidos en documentos. |
02-04 §6 |
| 5–6 (oct) | Sincronización: el monolito publica producto.actualizado en RabbitMQ (su primer evento) y Catálogo lo consume. RabbitMQ entra en producción aquí. |
03-02 |
| 7 | Comparación en sombra: el monolito lee de las dos fuentes y registra discrepancias. Se encontraron 212 productos con precio distinto (redondeo del script) y 3 eventos perdidos por un reinicio: se corrigió el script y se relanzó (idempotente). |
02-04 §6 |
| 8 | Corte de lecturas: el gateway enruta /api/v1/productos/* a Catálogo (strangler) y crearPedido pasa a RepositorioProductosHTTP con CATALOGO_REMOTO=true al 10 %, 50 %, 100 % en tres días. |
02-02 §4, 03-04 |
| 9 (nov) | Corte de escrituras: el panel de administración de productos pasa a servicio-catalogo; el monolito deja de escribir en productos. |
02-04 |
| 10–13 | La tabla productos queda en solo lectura; retirada el 15 de diciembre, tras confirmar que solo un informe mensual la usaba (se reescribió sobre una vista de solo lectura exportada por Catálogo; en la fase 6 pasaría a la BD de lectura del apartado 8). |
02-04 §6 |
El primer despliegue canary de TechCorp fue de Catálogo (05-04): 10 % de tráfico durante dos horas mirando el panel RED. Y el primer HPA también (06-04): minReplicas: 2, maxReplicas: 20, con la caché Redis de 30 s delante de MongoDB.
El examen llegó el 28 de noviembre de 2025. Con el catálogo separado y autoescalado, el Black Friday transcurrió así: navegación ×22 sobre un día normal, Catálogo pasó de 2 a 14 réplicas en 12 minutos, p95 de GET /v1/productos en 140 ms, y la creación de pedidos y los cobros —todavía en el monolito— no notaron nada: 0 minutos sin vender frente a los 50 del año anterior. Marta llevó ese dato a dirección y la migración dejó de discutirse. Fue también el momento en que quedó claro que el catálogo era el 70 % del coste de infraestructura de la campaña anterior: escalar solo lo que se satura costó un tercio que replicar el monolito a diez servidores.
- Fase 2: inventario, el primer consumidor de eventos (diciembre de 2025–enero de 2026)
servicio-inventario (3006, PostgreSQL) fue el primer servicio con estado de negocio delicado y el primero que consume eventos. Lo que cambió respecto al catálogo:
- Modelo de reservas. El monolito solo tenía
stock.reservado; el servicio nace con las tablasstock,reservas(conexpira_en) ylineas_reservade 02-04 §7.2. La reserva pasa de ser un contador a ser una entidad con estados ACTIVA/CONSUMIDA/LIBERADA (02-03) y con caducidad a los 900 s. - Branch by abstraction de la reserva. El módulo
almacencreado en la fase 0 recibió su segunda implementación (ReservaStockHTTP→POST /v1/reservasde 03-01) y la flagINVENTARIO_REMOTO. Durante tres semanas el monolito reservó por HTTP; elSIN_STOCKseguía siendo un409síncrono encrearPedido. - El primer consumidor. Como el monolito ya publicaba eventos desde la fase 1, Inventario empezó a consumir
pedido.confirmadoypedido.canceladode la colainventario.pedidospara consumir o liberar la reserva (03-02). Con ello llegaron la primera DLQ con mensajes (unpedido.canceladode un pedido cuya reserva se había hecho aún por la vía SQL antigua: mensaje venenoso, sin reserva que liberar) y la primera versión deprocesarUnaVezfuera de Pedidos. - La regla de Luis en acción. El outbox y la idempotencia de consumidores se habían escrito en Pedidos (todavía dentro del monolito) para publicar
pedido.confirmado; al necesitarlos en Inventario, se movieron a@techcorp/comun-http(mensajeria/outbox,mensajeria/idempotencia) en lugar de copiarlos. Es la versión que usan los cuatro servicios de 08-02. - KEDA (06-04) apareció aquí: escalar Inventario por la longitud de
inventario.pedidosfue más útil que hacerlo por CPU.
Las rebajas de enero (7 de enero de 2026) fueron la prueba de carga: pedidos ×3,5, Inventario de 2 a 6 réplicas, ninguna incidencia. La migración de datos de stock (12.000 filas) fue trivial comparada con la del catálogo: carga inicial, doble lectura en sombra durante una semana, corte.
- Fase 3: notificaciones, el correo fuera del proceso (febrero de 2026)
La extracción más corta (cuatro semanas) y una de las más agradecidas. servicios/correo.js y sus plantillas se convirtieron en servicio-notificaciones (3005), sin base de datos salvo la tabla envios para no reenviar (02-04 §7.4). Consume pedido.confirmado y pedido.cancelado de notificaciones.pedidos; el monolito dejó de llamar a correo.enviar() en crearPedido y pasó a confiar en el evento.
Dos efectos inmediatos:
- La fuga de memoria del correo dejó de ser un problema del monolito. El fallo del proveedor de correo volvió a ocurrir el 3 de marzo de 2026 (conexiones acumuladas): esta vez el pod de Notificaciones fue reiniciado por su
livenessProbedos veces, los mensajes esperaron en la cola y ennotificaciones.pedidos.reintento(06-03), y ningún cobro se vio afectado. El incidente 4 de 01-05, cerrado por diseño. - La respuesta de
crearPedidobajó 300 ms de media: el envío SMTP ya no estaba en la petición.
Fue también la fase en que Notificaciones estrenó los reintentos diferidos con TTL y la DLQ tal como quedaron en 06-03, porque un proveedor de correo devuelve 503 con frecuencia: reintentar de inmediato era inútil.
- Fase 4: pagos, dinero y cautela (marzo–abril de 2026)
servicio-pagos (3003, PostgreSQL) se llevó servicios/pasarelaPago.js y devolucionesControlador.js. Fue la extracción con más revisión y menos prisa, y la que más piezas de seguridad estrenó:
pagos.registrarCobro()→servicio-pagoscon branch by abstraction y flag; el monolito seguía siendo quien pedía cobrar (síncrono, dentro decrearPedido), pero quien hablaba con la pasarela ya era el servicio.- Tokenización y alcance PCI (07-03): el formulario de tarjeta pasó a hablar directamente con la pasarela; TechCorp solo recibe
tok_…. El alcance PCI DSS quedó reducido al servicio y a su Secretpagos-pasarela. - Webhook firmado
POST /v1/webhooks/pasarelacon HMAC, ventana temporal ywebhooks_recibidos(07-02 §9): la pasarela confirma cobros y devoluciones de forma asíncrona. - Auditoría (07-03 §9): tabla
auditoriade solo inserción para cambios manuales de estado de pago y reembolsos. UNIQUE (pedido_id)enpagosy clave de idempotencia =pedidoIdhacia la pasarela (06-03 ej. 1): la garantía de "un pedido, un cobro" pasó de la transacción del monolito a dos restricciones explícitas.- Despliegue canary obligatorio (05-04) con revisión humana del PR de promoción (05-03 §8), como corresponde a un servicio que mueve dinero.
Un dato: el equipo dedicó una semana entera a la prueba de resiliencia (06-03 §11) contra la pasarela ficticia —503, timeouts, respuestas duplicadas— antes de mover un céntimo real. Ningún cobro duplicado en producción desde entonces.
- Fase 5: pedidos, la saga sustituye a la transacción (mayo–julio de 2026)
La fase central, la más larga (once semanas) y la que da sentido a las anteriores. Cuando llegó, Catálogo, Inventario, Notificaciones y Pagos ya eran servicios y el monolito ya publicaba y consumía eventos: crearPedido seguía siendo una función que llamaba a los demás por HTTP y coordinaba con su transacción local. Lo que cambió:
servicio-pedidos(3002) tal como se construyó en 04-04: agregadoPedido, máquina de estados de 02-05,POST /v1/pedidos→202, outbox con relay, consumidorespedidos.sagaypedidos.clientes.- La saga por coreografía sustituyó a la transacción: Inventario pasó a reaccionar a
pedido.creado(en vez de recibirPOST /v1/reservassíncrono), Pagos astock.reservado(en vez de la llamada del monolito), Pedidos apago.confirmado. La flagSAGA_ASINCRONAen el monolito permitió, durante dos semanas, que un porcentaje de pedidos siguiera la vía nueva mientras el resto usaba la antigua; el panel "Saga de pedidos" (06-01) comparaba ambos. - El cambio de contrato visible:
POST /pedidosdejó de responder201 CONFIRMADOy pasó a202 PENDIENTE+ polling con ETag (03-01). Fue el único cambio de la migración que exigió coordinación con el equipo de front-end y con la app móvil (a través delbff-movil, 03-04): dos sprints de aviso, cabeceraDeprecationen la ruta antigua (03-06). - El vigilante de
TIMEOUT_PAGO(06-03 §9) y elCronJob reconciliar-reservasse escribieron antes del corte, no después de un incidente: la saga por coreografía sin vigilante es una saga que se atasca. - Corte del strangler:
/api/v1/pedidos/*al servicio el 15 de junio de 2026 al 10 %, 100 % el 19;SAGA_ASINCRONA=trueen el 100 % el 26 de junio; migración de los 2,1 millones de pedidos históricos a la BDpedidosen tres noches por lotes (carga inicial idempotente + eventos de cambio de estado en sombra); retirada del código decrearPedidodel monolito el 10 de julio.
Y el 22 de julio de 2026, INC-2031 (06-05 §10): un stock.reservado con lineas: null publicado por Inventario 2.3.0 atascó pagos.stock 40 minutos; 61 pedidos afectados, 38 cancelados por el vigilante. Es el incidente que dio forma definitiva a la operación: colas de reintento y DLQ a la primera para errores permanentes en Pagos, alertas DlqConMensajes y SagaTardiaBurn*, validación de eventos de salida contra el esquema AsyncAPI en Inventario, y el runbook comun/dlq con scripts/reprocesarDlq.js. Sin INC-2031, media lección 06-05 no existiría; con él, el sistema quedó como está hoy.
- Fase 6: clientes y el apagado del monolito (julio–agosto de 2026)
servicio-clientes (3004) fue el último por las razones de 02-02: identidad y datos personales, y cliente_id estaba en todas partes. Al ser el último, se benefició de todo lo anterior:
- Keycloak (07-01) había entrado en producción al inicio de la fase 5, federando las credenciales de la tabla
clientesdel monolito para poder proteger/api/v1/pedidos/*con JWT. En la fase 6 se completó: las identidades se migraron al realmtechcorp, el alta de cliente pasó aservicio-clientes, que escribe el atributoclienteIden Keycloak, y el claimclienteIdviaja en el token; el monolito dejó de tener sesión. clientes_refen Pedidos (02-04) se venía alimentando concliente.actualizadopublicado por el monolito desde la fase 5; solo cambió el productor.- RGPD (07-03 §8):
DELETE /v1/clientes/{id}→ anonimización +cliente.eliminado; Pedidos borra la réplica y anonimiza direcciones antiguas. GET /v1/clientes/{id}con la regla "propio cliente, operador o token de servicio conclientes:leer" (07-01 ej. 2), yPOST /v1/pedidoscon la comprobaciónclienteIddel cuerpo =clienteIddel token.
El apagado del monolito ocurrió el 7 de agosto de 2026. Lo que quedaba dentro en ese momento y adónde fue:
| Resto en el monolito | Destino |
|---|---|
Informes de ventas y paneles de dirección (los JOIN de cuatro tablas de 02-04 §8) |
Base de datos de lectura techcorp-analitica (PostgreSQL) alimentada por eventos (pedido.confirmado, producto.actualizado, cliente.actualizado) con un consumidor propio; los informes se reescribieron contra ella. Es el CQRS "a escala de empresa" de 02-05. |
| Panel de administración interno (varias áreas) | Aplicación web ligera que consume las APIs por el gateway con rol operador. |
| Exportación nocturna al ERP | Consumidor de eventos que genera el fichero. |
Tabla pedidos histórica en solo lectura |
Migrada a pedidos en la fase 5; la copia del monolito se conservó un mes y se archivó. |
# 2026-08-07 10:30 — el último commit del monolito: el gateway deja de tener ruta comodín
kubectl -n techcorp scale deployment techcorp-shop --replicas=0 # nadie lo nota: hace tres semanas que no recibe tráfico
# 2026-08-21 — tras dos semanas a cero réplicas sin ninguna petición al comodín (métrica gateway_rutas_monolito_total = 0): borrado
kubectl -n techcorp delete deployment,service,configmap -l app=techcorp-shopEse scale --replicas=0 durante dos semanas antes de borrar es la última aplicación del principio de la fase 1: no tener prisa en retirar lo antiguo.
- Tabla cronológica de la migración
| Fase | Fechas | Servicio | Técnica principal | Lecciones | Riesgo principal | Resultado |
|---|---|---|---|---|---|---|
| 0 | jul–sep 2025 | — (monolito modular, plataforma) | CI, contenedores, K8s + gateway delante, observabilidad mínima, cuatro equipos, devolver escrituras de stock/pagos a su dueño |
01-04, 02-02, 03-04, 05-01/02/03, 06-01 | Tres meses "sin resultados" | Base para todo lo demás; suite de 40 → 12 min |
| 1 | sep–nov 2025 | servicio-catalogo |
Strangler de /productos, RepositorioProductosHTTP + CATALOGO_REMOTO, carga inicial + eventos + sombra, HPA, Redis |
02-02, 02-04, 04-02, 05-04, 06-04 | Discrepancias de datos | Black Friday sin caída (0 min vs 50); coste de campaña ÷3 |
| 2 | dic 2025–ene 2026 | servicio-inventario |
Reservas con expira_en, INVENTARIO_REMOTO, primer consumidor, primera DLQ, KEDA; outbox/idempotencia a comun-http |
02-04, 03-01, 03-02, 06-04 | Vender lo que no hay | Rebajas ×3,5 sin incidencia |
| 3 | feb 2026 | servicio-notificaciones |
Consumidor de pedido.confirmado/cancelado, reintentos con TTL + DLQ |
03-02, 06-03 | Correos duplicados/perdidos | Fuga de memoria aislada; crearPedido −300 ms |
| 4 | mar–abr 2026 | servicio-pagos |
Tokenización, webhook HMAC, auditoría, UNIQUE(pedido_id), canary con revisión |
05-04, 06-03, 07-02, 07-03 | Cobros duplicados / PCI | Cero cobros duplicados; alcance PCI mínimo |
| 5 | may–jul 2026 | servicio-pedidos |
Saga por coreografía, outbox, 202 + ETag, vigilante, reconciliación, SAGA_ASINCRONA |
02-05, 03-01, 04-04, 06-03, 06-05 | Sagas atascadas | Pedidos desplegable a diario; INC-2031 (40 min) y su postmortem |
| 6 | jul–ago 2026 | servicio-clientes + apagado |
Keycloak + clienteId, clientes_ref, RGPD, BD de lectura para BI |
02-04, 07-01, 07-03 | Datos personales | Monolito a 0 réplicas el 7/8/2026 |
- Métricas antes y después
Las cuatro métricas DORA de 05-03 §12 medidas en agosto de 2026 (media de los seis servicios), más las de negocio y organización que Marta presentó a dirección:
| Métrica | Antes (junio 2025) | Después (agosto 2026) | Cómo se mide |
|---|---|---|---|
| Frecuencia de despliegue | 1 cada 1–2 semanas (jueves noche), toda la tienda | 11 al día en total (Catálogo 3–4, Pedidos 2–3, resto 1–2) | Syncs de Argo CD por servicio |
| Lead time de cambios | 9 días de mediana | 4 horas de mediana (23 min para hotfixes) | Fecha de commit → fecha de sync |
| Tasa de fallo de cambios | 33 % (4/12 reversiones) | 6 % (5 reversiones/rollbacks de 82 despliegues en julio) | Incidentes o rollbacks / despliegues |
| MTTR | 50 min (Black Friday); ~2 h de mediana en despliegues fallidos | 9 min de mediana (argocd app rollback o canary-weight=0) |
Desde la alerta hasta el cierre |
| Disponibilidad de "crear pedido" | 99,5 % mensual estimada (no se medía) | 99,93 % (SLO 99,9 %, 06-05) | SLI slo:pedidos_disponibilidad |
| Latencia catálogo p99 en campaña | > 8 s (saturación) | 280 ms | SLI de latencia |
| Tiempo de coordinación (equipo de Pedidos) | ~20 % | ~7 % (revisiones de contrato y PRs de promoción) | Encuesta trimestral + tiempo en PRs cruzados |
| Suite de pruebas | 40 min (todo) | 4–7 min por servicio | CI |
| Coste infraestructura de campaña | ×3,3 (diez servidores del monolito) | ×1,4 (solo Catálogo e Inventario escalan) | Factura del proveedor |
Dos avisos honestos que Marta añadió a la tabla: el "antes" de disponibilidad es una estimación (no había SLI, que es en sí mismo un dato), y la tasa de fallo del 6 % incluye INC-2031, que fue el peor incidente del año y aun así duró menos que el mejor despliegue de jueves.
- Costes reales y lo que salió mal
Lo que costó. Un año de migración no sale gratis, y conviene tener las cifras (ficticias, pero de orden realista) para el ejercicio de 08-04:
| Coste | Magnitud | Comentario |
|---|---|---|
| Tiempo de equipo | ~40 % de la capacidad de desarrollo durante 13 meses (unas 10 personas equivalentes) | La mitad en la fase 5. El resto siguió entregando funcionalidad; el negocio no se paró. |
| Infraestructura | +35 % el primer año (clúster, RabbitMQ, observabilidad, Keycloak, entornos de staging); −20 % respecto al año anterior en campañas | Con el catálogo escalado por separado, el balance neto al final del año fue ligeramente positivo por el ahorro en campañas. El coste mensual detallado está en 08-03. |
| Curva de aprendizaje | Kubernetes, RabbitMQ, OpenTelemetry, Keycloak, Pact: cada uno costó a alguien 2–4 semanas | Mitigado por Plataforma (plantillas, workflow reutilizable) y por hacerlo en orden. |
| Herramientas de pago | Registro privado, Pact Broker gestionado, gestor de secretos | Menores; se justificaron una a una. |
Lo que salió mal. Tres tropiezos que el equipo cuenta sin vergüenza porque son los que más enseñan:
- El dual write que se intentó (fase 1, octubre de 2025). La primera versión de la sincronización del catálogo escribía la tabla
productosy el documento de MongoDB en la misma función del monolito, "para no montar RabbitMQ todavía". En una semana la comparación en sombra encontró 40 productos divergentes: un timeout de MongoDB, una excepción entre las dos escrituras, y ninguna transacción que las cubriera (02-04 §6). Se tiró el código, se adelantó RabbitMQ y se publicóproducto.actualizadodesde la fuente de verdad. La lección se convirtió en regla escrita: una escritura, en el dueño; la copia, por eventos. servicio-descuentos, el nanoservicio (fase 4, abril de 2026). Marketing pidió cupones para las rebajas de primavera y el equipo de Experiencia de compra, con ganas de estrenar plantilla, creó un servicio con una tablacuponesy un único endpointPOST /v1/descuentos/calcular, llamado de forma síncrona porcrearPedidoen cada pedido. Añadió 40 ms a cada pedido, un despliegue más, una dependencia más en el camino crítico y ninguna capacidad de negocio autónoma (02-01: "¿se describe en una frase sin 'y'?" sí, pero "¿tiene datos y ciclo de vida propios?" no). En la fase 5 se reabsorbió enservicio-pedidoscomo módulodominio/descuentos.jscon la tabla dentro de la BDpedidos, exactamente lo que el ejercicio 2 de 02-02 había recomendado. Si algún día las promociones crecen (campañas, reglas, segmentos), volverán a salir; hoy son doce reglas y una tabla.- La cardinalidad que tumbó Prometheus (fase 3, febrero de 2026). Un consumidor de Notificaciones etiquetó
http_requests_totalcon la ruta sin plantilla (/v1/pedidos/ped-3f9a1c2ben lugar de/v1/pedidos/{id}) y otro añadiópedidoIdcomo etiqueta a un contador. En cinco días Prometheus pasó de 60.000 a 2,4 millones de series y el pod murió por memoria; durante 25 minutos no hubo métricas ni alertas. La corrección (06-01 §11): etiquetas solo con valores acotados,rutadesdereq.route.path, una regla de linting de métricas encomun-httpy un límitesample_limiten el scrape. Es el motivo por el que la métricapedidos_cancelados_totalllevamotivo(cuatro valores) y nuncapedidoId.
Y un cuarto, menor pero frecuente: durante dos meses convivieron el ci.yml de 80 líneas copiado en tres repositorios con tres pequeñas diferencias; el workflow reutilizable de 05-03 §11 llegó "antes del cuarto", no "antes del segundo". La regla de Luis también se incumple a veces.
- La arquitectura final
Así queda TechCorp en agosto de 2026, con el monolito apagado. Comparado con el diagrama de 01-05 §7 (el objetivo) hay tres diferencias: no hay Istio (05-05: evaluado y pospuesto; mTLS con Linkerd cuando lo pida la auditoría de Pagos), sí hay una BD de lectura para analítica, y Redis, Keycloak y la pila de observabilidad tienen nombre propio.
flowchart TB
subgraph Externo
WEB[tienda-web / bff-movil :3010]
PSP[Pasarela de pago]
SMTP[Proveedor de correo]
end
WEB -->|HTTPS + JWT| ING[Ingress api.techcorp.example<br/>cert-manager]
ING --> GW[API Gateway :8080<br/>requiereToken, rate limit]
KC[Keycloak realm techcorp] -. JWKS .-> GW
GW -->|/api/v1/productos| CAT[servicio-catalogo :3001<br/>HPA 2-20 + Redis]
GW -->|/api/v1/pedidos| PED[servicio-pedidos :3002<br/>canary]
GW -->|/api/v1/clientes| CLI[servicio-clientes :3004]
PSP -->|webhook HMAC| PAG
CAT --> MDB[(MongoDB catalogo)]
PED --> PGP[(PG pedidos)]
CLI --> PGC[(PG clientes)]
INV[servicio-inventario :3006<br/>KEDA 2-10] --> PGI[(PG inventario)]
PAG[servicio-pagos :3003<br/>canary] --> PGG[(PG pagos)]
NOT[servicio-notificaciones :3005] --> SMTP
PAG --> PSP
PED -->|GET productos / clientes| CAT
PED --> CLI
PED <-.-> MQ[(RabbitMQ techcorp.eventos<br/>amqps, vhost techcorp)]
MQ <-.-> INV
MQ <-.-> PAG
MQ -.-> NOT
MQ -.-> ANA[consumidor analitica] --> PGA[(PG techcorp-analitica<br/>informes / BI)]
CLI -.->|cliente.actualizado / eliminado| MQ
subgraph Plataforma
OBS[Prometheus · Grafana · Loki · otel-collector · Jaeger]
ARGO[Argo CD ← techcorp/plataforma]
VAULT[Vault + ESO]
end
Lo que no se ve en el diagrama y es tan importante como lo que se ve: las NetworkPolicies deny-all de 07-04 (a Inventario solo le habla Pedidos; solo Pagos sale a Internet), los SLOs y las once alertas de 06-05, los runbooks en techcorp/plataforma/runbooks/, y cuatro equipos que despliegan sin pedir permiso a nadie.
Errores Comunes y Consejos
- Saltarse la fase 0. Es la tentación más fuerte ("empecemos por algo visible") y la que más caro se paga: sin gateway no hay strangler, sin CI no hay confianza, sin observabilidad no hay diagnóstico. Tres meses de deberes ahorraron un año.
- Extraer el core primero. Pedidos fue el quinto porque dependía de todos; extraerlo primero habría producido un monolito distribuido con cinco llamadas al monolito por pedido.
- Borrar lo antiguo el día del corte. La tabla
productosen solo lectura durante seis semanas y el monolito a cero réplicas durante dos evitaron dos incidentes seguros (el informe mensual y una integración olvidada). - Medir solo al final. El "antes" de disponibilidad es una estimación porque no se medía. Instrumenta el monolito antes de tocarlo: es tu línea base y tu argumento ante dirección.
- Confundir tropiezo con fracaso. El dual write, el nanoservicio y la cardinalidad costaron días, no meses, porque cada uno se detectó con una herramienta que ya existía (sombra, panel RED, alerta de Prometheus caído). El objetivo no es no equivocarse: es equivocarse barato y aprender por escrito.
- Consejo: guarda una tabla cronológica como la del apartado 9 desde el primer día, con la columna "riesgo principal" rellena antes de empezar cada fase. Es la mejor herramienta de comunicación con negocio y el mejor material para el postmortem del proyecto.
Ejercicios
Ejercicio 1: Reordenar con otra restricción
Imagina que TechCorp hubiera tenido, en julio de 2025, una auditoría PCI obligatoria en enero de 2026. ¿Cambiaría el orden de extracción? Propón un orden alternativo, indica qué fase se adelanta y a qué coste, y qué parte de la fase 0 pasaría a ser imprescindible antes de esa fase.
Ejercicio 2: Diagnosticar un tropiezo
Un equipo cuenta que, durante su migración de un catálogo, "los datos del servicio nuevo se desincronizaban de vez en cuando y nadie sabía por qué". Con lo aprendido en las fases 1 y en el apartado 11: enumera las tres causas más probables, la herramienta que las habría detectado y la técnica que las evita.
Ejercicio 3: Leer la tabla de métricas
Con la tabla del apartado 10: (a) ¿qué métrica demuestra que la fase 1 resolvió el problema 2 de 01-05? (b) ¿por qué la tasa de fallo del 6 % es mejor aunque en número absoluto de despliegues fallidos (5 en julio) sea mayor que antes (4 en un trimestre)? (c) ¿Qué métrica de la tabla es la que menos fiable y por qué?
Soluciones
Ejercicio 1. Sí: Pagos pasaría del cuarto al segundo puesto (tras Catálogo), para que en enero la pasarela, la tokenización, el webhook HMAC, la auditoría y el securityContext/NetworkPolicies de Pagos estuvieran aislados en un servicio con alcance PCI mínimo. Coste: Pagos se extraería con branch by abstraction desde el monolito (como en la fase 4 real) pero antes de que existiera Inventario, así que seguiría cobrando por llamada síncrona desde crearPedido más tiempo, y su consumidor de stock.reservado se escribiría más tarde (doble trabajo en el adaptador). Además, el equipo estrenaría canary y revisión humana con el servicio más delicado, en lugar de entrenar con Catálogo. De la fase 0 pasarían a imprescindibles antes de Pagos: la retirada de la escritura en pagos desde crearPedido (pagos.registrarCobro()), la gestión de secretos (07-04: el Secret pagos-pasarela no puede vivir en un YAML) y las NetworkPolicies de "solo Pagos sale a Internet".
Ejercicio 2. (1) Dual write: el monolito escribe en las dos fuentes sin transacción común; una excepción o timeout entre ambas deja copias divergentes. Lo detecta la comparación en sombra; lo evita "una escritura en el dueño + eventos" (02-04 §6). (2) Eventos perdidos: el productor publica sin outbox (o el consumidor hace ack antes de escribir) y un reinicio pierde mensajes. Lo detecta la sombra y la métrica outbox_pendientes/mensajes sin ack; lo evita el outbox transaccional (02-05 §7) y ack tras procesar (03-02). (3) Eventos desordenados o duplicados que pisan un valor nuevo con uno viejo. Lo detecta la sombra (discrepancias intermitentes que "se arreglan solas" al siguiente cambio); lo evita procesarUnaVez + comparación por actualizadoEn (04-04 ej. 2). Añadido: un script de carga inicial no idempotente que se relanzó a medias (duplicados o valores antiguos); lo evita upsert (02-04).
Ejercicio 3. (a) "Latencia catálogo p99 en campaña" (> 8 s → 280 ms) y sobre todo el dato de negocio del apartado 3: 0 minutos sin vender el 28/11/2025 frente a 50; el coste de campaña ×3,3 → ×1,4 confirma que se escala solo lo que se satura. (b) Porque la tasa se mide por despliegue y el número de despliegues se ha multiplicado por más de 20: 5 fallos de 82 (6 %) frente a 4 de 12 (33 %). Además, cada fallo afecta a un servicio, se detecta en un canary al 10 % y se revierte en minutos (MTTR 9 min), frente a revertir toda la tienda tras dos horas. Es la relación DORA de 05-03: más despliegues pequeños → menos fallo y menos MTTR. (c) La disponibilidad "antes" (99,5 %): es una estimación retrospectiva, porque el monolito no tenía SLI; y en menor medida el tiempo de coordinación, que se basa en encuestas. La propia tabla lo advierte: sin medición previa no hay línea base fiable, y esa es una lección en sí misma.
Conclusión
La migración de TechCorp no fue una reescritura sino trece meses de extracciones ordenadas sobre una fase 0 de deberes que no produjo ningún servicio y lo hizo posible todo: cuatro equipos dueños de sus áreas, CI y contenedores, un clúster con el gateway delante como fachada del strangler, observabilidad mínima y un monolito modular en el que crearPedido dejó de escribir en stock y pagos. Después, cada fase resolvió un problema concreto de 01-05 con las técnicas de los módulos anteriores: Catálogo (carga inicial, eventos, sombra, flag, HPA) y el primer Black Friday sin caída; Inventario (reservas con caducidad, primer consumidor, primera DLQ, KEDA); Notificaciones (el correo fuera del proceso, fin de la fuga que tumbaba cobros); Pagos (tokenización, webhook HMAC, auditoría, canary con revisión); Pedidos (la saga por coreografía con outbox y vigilante en lugar de la transacción, el 202, e INC-2031 como maestro); Clientes (Keycloak, clienteId, RGPD) y el apagado del monolito con la analítica trasladada a una base de datos de lectura. Las métricas cuentan el resultado —de un despliegue quincenal a once al día, de 9 días a 4 horas de lead time, del 33 % al 6 % de fallos, de 50 a 9 minutos de recuperación, 99,93 % de disponibilidad de "crear pedido"— y los tropiezos (el dual write, servicio-descuentos, la cardinalidad) cuentan lo que costó aprenderlo.
Este relato ha remitido constantemente a código que ya existe (servicio-catalogo de 04-02, servicio-pedidos de 04-04, el gateway de 03-04 y 07-01) y a cuatro servicios que hemos descrito pero nunca escrito: Inventario, Pagos, Notificaciones y Clientes. La siguiente lección los implementa de forma condensada pero completa, con la misma plantilla y la misma librería, cierra el mapa de eventos y sigue el pedido ped-88213 por los seis servicios reales, base de datos a base de datos y cola a cola.
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
