Módulo 12 · Nivel empresarial (integración)

Lección 53 — Monolito modular frente a microservicios

Una decisión razonada, no una moda: cuándo trocear y cuándo no.

Publicada
En esta lección
  1. Objetivos
  2. 1. La comparación honesta: lo que cada uno compra y cobra
  3. 2. El monolito modular de TicketFlow: los módulos y sus fronteras
  4. 3. Los contratos entre módulos: síncronos y asíncronos
  5. 4. El umbral de extracción: cuándo UN servicio sale
  6. 5. Lo que el monolito modular te regala HOY
  7. Autoevaluación

Stack: Django/DRF · Proyecto: TicketFlow Estado: Publicada — una decisión razonada, no una moda Prerrequisito: Lección 52 — Clean/Hexagonal y DDD aplicados


Objetivos

  1. Comparar monolito modular y microservicios con los costos REALES de cada uno (red, datos, operación, equipo), sin el marketing de Netflix.
  2. Modularizar TicketFlow como monolito modular: los módulos con fronteras explícitas y el contrato entre ellos.
  3. Identificar los umbrales que justifican extraer UN servicio (el strangle) y el camino de salida si algún día hace falta.

1. La comparación honesta: lo que cada uno compra y cobra

DimensiónMonolito modularMicroservicios
Transacciones (10)Una BD, ACID localSagas/composición eventuales (32) EN CADA flujo
Red entre piezasLlamadas de función (0 ms, 0 fallos)Latencia + timeouts + breakers (54) en cada borde
DatosUn schema, joins libresBD por servicio, duplicación + sincronización (25)
DespliegueUno (41: blue-green de todo)N pipelines, versionado de contratos (14)
Operación1 app, 1 dashN apps, N dash, service mesh, el 46×N
Equipo1-10 devs felicesLa orquesta necesita director y músicos por servicio
Independencia de despliegueNinguna (un merge = un deploy)Total (el gran beneficio real)
FronterasDisciplina (el linter/las reglas)Físicas (la red las obliga)

El punto que el marketing esconde: los microservicios NO arreglan el acoplamiento — lo hacen PAGABLE: lo que en el monolito era un mal import (el 24: la vista que consulta el ORM de otro módulo) en microservicios es una llamada de red que FALLA con latencia y retries. Si tus fronteras conceptuales son malas, las físicas las harán malas Y lentas. El orden correcto: fronteras modulares primero (hoy), fronteras físicas si el negocio las exige (mañana).

2. El monolito modular de TicketFlow: los módulos y sus fronteras

La modularización sobre lo que el curso ya hizo (los bounded contexts del 52):

ticketflow/
  tickets/      # el dominio del asiento: reserva, disponibilidad, expiración (10/25/31)
  payments/     # el dinero: pasarela, intent, refunds, comisiones (32/08)
  events/       # el catálogo: eventos, venues, precios (00b)
  identity/     # auth, MFA, roles (18-21)
  shared/       # el dominio compartido: Money, Clock, outbox, el handler del 26

Las reglas de frontera (la constitución del monolito modular, aplicable con tests): (1) un módulo habla con otro por SU interfaz pública (el servicio de la 24) — jamás por sus models/managers internos; (2) los eventos entre módulos van por el outbox/stream (25/30) — jamás por import directo del módulo ajeno en el flujo de negocio; (3) la BD: UN schema (por ahora) con los FK entre módulos prohibidos (la referencia es por UUID público, la 13: el FK crea el acoplamiento físico que mañana impide extraer); (4) el shared/ es pequeño y estable (el Money, el Clock): el cajón de sastre del 24 es el anti-patrón de la modularidad.

python
# tickets/api.py — la interfaz pública del módulo (lo que otros importan)
def reservar(user, event_uuid, seat_refs, *, clock=None) -> ReservationSummary: ...
def disponibilidad(event_uuid) -> list[SeatFree]: ...

# y el import que la constitución prohíbe (el test del CI lo caza, 41):
# from tickets.models import ReservationModel   ← en payments/: PROHIBIDO

El test de frontera (el que hace la modularidad REAL):

python
def test_modulos_no_importan_entres_si():
    with open("payments/services.py") as f:
        assert "from tickets.models" not in f.read()      # la interfaz o nada

3. Los contratos entre módulos: síncronos y asíncronos

La comunicación entre módulos: síncrona (la función pública: el checkout de payments llama a tickets.reservar(): la transacción es UNA — el mayor regalo del monolito) y asíncrona (los eventos del outbox: PaymentSucceeded llega a tickets sin acoplamiento del emisor al receptor — la coreografía del 32). La regla de elección: síncrono cuando la operación pertenece a la MISMA transacción (el asiento y el estado de la compra); asíncrono cuando el receptor es periférico (emails, analytics) o el acoplamiento temporal es aceptable. Y el contrato de cada interfaz: testado (la 35: el test de contrato entre módulos = el pacto que el día del servicio externo será HTTP).

La decisión del equipo de 1 (el 51): el monolito modular compra el 90% del beneficio (fronteras, dominios separados, extraíbles mañana) por el 10% del costo. Los microservicios de TicketFlow HOY: 5 despliegues, 5 pipelines, la saga del 32 en CADA flujo de negocio, y 1 solo dev orquestándolo: la factura de operación supera el beneficio por un orden de magnitud.

4. El umbral de extracción: cuándo UN servicio sale

El strangle pattern (la salida progresiva): extraer el módulo cuando su contexto exige FÍSICA: (1) el ciclo de vida diverge (payments despliega 10×/día por compliance mientras tickets 2×/mes: el monolito acopla los ritmos); (2) el escalado diverge (payments necesita 3 réplicas y tickets 1: el monolito escala la caja entera, el 55); (3) la tecnología diverge (el motor de pricing en Go/C: la frontera del lenguaje); (4) la seguridad diverge (payments con acceso a PCI: la frontera del cumplimiento, el 56); (5) el equipo diverge (2 equipos que pelean el mismo repo: Conway lo decide). TicketFlow HOY: nada diverge — el trigger documentado (el ADR): "se extrae payments cuando el PCI del 56 lo exija o el volumen de pagos ×10 frente al resto".

El strangle (la extracción sin big-bang): (1) el módulo ya tiene interfaz pública (§2: la frontera hecha desde hoy); (2) el servicio nuevo DUPLICA la funcionalidad con el flag del 27 decidiendo quién sirve; (3) el tráfico migrado por porcentaje (el canary del 46); (4) el monolito destrona el código (el 11: expand-contract aplicado a la arquitectura); (5) la limpieza. La extracción de UN módulo con la frontera preparada: 2-4 semanas (la estimación del 49); sin ella: 2-4 meses (el re-write del acoplamiento escondido).

5. Lo que el monolito modular te regala HOY

La lista concreta de TicketFlow modular: el dominio del asiento (tickets) testable sin tocar el dinero (payments); el onboardinng del compañero nuevo (48): 5 módulos con ADRs vs un repo anónimo; el deploy UNO (41) con las 5 métricas DORA sanas; el costo de la nube del 42 (1 PaaS, no 5); y la salida abierta: cada módulo con su interfaz es el candidato al strangle cuando el trigger llegue — el monolito modular NO es "microservicios aplazados por pereza": es la decisión INFORMADA de pagar la red solo cuando la red pague su costo (el 51: cada pieza, su línea de negocio).


Autoevaluación

  1. ¿Qué compra y qué cobra cada modelo en las 8 dimensiones del §1? ¿Qué hace el microservicio con el acoplamiento que el monolito ignora?
  2. Enumera las 4 reglas de frontera del monolito modular de TicketFlow y qué test del CI las hace reales.
  3. ¿Cuándo síncrono y cuándo asíncrono entre módulos, y qué regalo del monolito se pierde al hacerlo async?
  4. Los 5 umbrales del strangle: ¿cuál es el más probable en TicketFlow y qué documento lo activa?
  5. ¿Qué pasos tiene el strangle y cuánto cambia el costo de extraer CON frontera preparada vs SIN ella?

Continúa con los ejercicios. Las solutions.md solo tras intentarlo.