volver al blog

El coste oculto de los microservicios que nadie te factura

arquitecturamicroserviciosbackend

La factura que no viene en la propuesta

Los microservicios se venden como libertad. Cada equipo despliega a su ritmo, cada servicio escala por su cuenta, cada dominio vive en su repo. Suena a madurez de ingeniería.

Lo que rara vez aparece en la propuesta es la factura. Y no la de AWS, que también. Hablo de la factura operativa: la que pagas en latencia, en trazas perdidas, en reuniones para acordar quién es dueño de qué campo. Esa no la firma nadie, pero la paga el equipo entero, mes a mes.

He llevado a producción sistemas de los dos tipos. La conclusión no es “los microservicios son malos”. Es que casi siempre se adoptan por la razón equivocada, antes de tiempo, y el coste llega tarde y de golpe.

La llamada de función que ahora es una petición de red

Este es el cambio que la mayoría subestima. En un monolito, cuando el módulo de pedidos necesita datos de inventario, es una llamada de función: nanosegundos, tipada, o compila o no compila.

Al partirlo, esa misma llamada se convierte en una petición de red. Y una petición de red puede fallar, tardar, o volver a medias.

# Monolito: si esto rompe, rompe en tiempo de compilación
stock = inventario.consultar_stock(producto_id)

# Microservicios: lo mismo, pero ahora todo esto es tu problema
try:
    resp = await http.get(
        f"http://inventario/stock/{producto_id}",
        timeout=2.0,  # ¿y si tarda más? ¿reintentas? ¿cuántas veces?
    )
    resp.raise_for_status()
    stock = resp.json()["stock"]
except (TimeoutError, HTTPError):
    # ¿Qué hago aquí? ¿Fallo el pedido? ¿Uso un valor cacheado?
    # ¿Asumo que hay stock y corrijo luego? No hay respuesta gratis.
    stock = fallback_stock(producto_id)

Multiplica ese bloque try/except por cada frontera entre servicios. Timeouts, reintentos, idempotencia, circuit breakers, colas de reintento. Nada de eso aportaba valor de negocio en el monolito porque, sencillamente, no existía. Ahora es código que mantienes, pruebas y depuras a las tres de la mañana.

Una transacción que ya no es una transacción

En un monolito, “cobra y descuenta stock” cabe en una transacción de base de datos. O pasan las dos cosas o no pasa ninguna. El motor de la base de datos te regala esa garantía.

Reparte pagos e inventario en dos servicios con dos bases de datos y esa garantía desaparece. Bienvenido a la consistencia eventual, a los patrones saga y a la pregunta incómoda: ¿qué hago si cobré pero no pude reservar el stock?

No hay una respuesta cómoda. Hay compensaciones, reembolsos automáticos y estados intermedios que tu producto tiene que saber representar. Es resoluble, lo resolvemos a diario, pero es trabajo real que nace el día que dibujas la frontera. Nadie te lo factura por adelantado.

Depurar deja de ser leer un stack trace

En un monolito, un error deja un rastro claro: una traza que puedes leer de arriba abajo. En un sistema distribuido, una sola petición de usuario puede tocar seis servicios, y el fallo asoma tres saltos más allá de donde nació.

Sin trazado distribuido estás ciego. Así que montas OpenTelemetry, un colector, IDs de correlación que viajan por cada cabecera y un backend donde mirar todo eso. Es tooling que en el monolito no necesitabas, y ahora es requisito para no depurar a base de adivinar.

Cuándo sí, cuándo no

La pregunta útil no es “¿monolito o microservicios?”. Es “¿qué me está doliendo de verdad y esto lo cura?”.

SeñalMonolitoMicroservicios
Tamaño del equipo1 a 15 personas en un mismo códigoVarios equipos que se pisan al desplegar
EscaladoTodo escala parecidoUna parte necesita escalar sola (picos, GPU)
Ritmo de despliegueUn despliegue coordinado bastaCada equipo necesita su propio ciclo
Madurez operativaCI/CD básico y un logObservabilidad, on-call y automatización ya en marcha
DominiosFronteras aún borrosasLímites claros y estables
Coste principalAcoplamiento a la largaComplejidad distribuida desde el minuto uno

La regla que sigo: los microservicios resuelven un problema organizativo, no técnico. Aparecen cuando demasiada gente comparte demasiado código y los despliegues se convierten en negociaciones. Si tu problema es de rendimiento o de orden interno, un monolito bien modularizado casi siempre gana, con menos partes móviles.

El monolito modular: casi todo el beneficio, casi ningún coste

Hay un punto intermedio que se menciona poco: el monolito modular. Un solo despliegue, una sola base de datos, pero con fronteras internas serias entre módulos. Interfaces explícitas, nada de importar tablas ajenas a discreción, cada módulo dueño de su esquema.

Te llevas el 80 % del beneficio de los microservicios —código organizado, límites claros, dominios separados— sin la red de por medio. Y el día que un módulo de verdad necesite salir a su propio servicio, la frontera ya está dibujada. Extraerlo es refactor, no reescritura.

monolito/
  modulos/
    pedidos/      # dueño de sus tablas; expone una interfaz, no su BD
    inventario/   # otros módulos llaman a inventario.stock(), no a SELECT
    pagos/        # frontera lista para extraer el día que toque
  compartido/     # solo tipos y utilidades, cero lógica de dominio

La conclusión

Los microservicios no son una insignia de madurez. Son una herramienta con un coste operativo alto y muy real que casi nunca aparece en la decisión inicial.

Antes de partir el monolito, plantéate tres cosas. Si un servicio se cae a las tres de la mañana, ¿alguien de guardia sabrá por qué? ¿Tienes trazado distribuido antes de necesitarlo, o lo montarás con el sistema ya ardiendo? ¿El dolor que quieres curar es de despliegue y equipos, o solo de código desordenado?

Si la respuesta a la última es “código desordenado”, ordénalo primero. Un monolito modular arreglará el 80 % por una fracción del coste. Los microservicios seguirán ahí cuando de verdad los necesites, y para entonces sabrás exactamente por qué los pagas.