Regresar
101

Monolito vs microservicios vs monolito modular

Actualizado: 14/09/2026

Segunda semana de un proyecto nuevo. Todavía no hay usuarios, el equipo son dos personas, y en la pizarra ya hay seis cajas con flechas entre ellas: pedidos, usuarios, notificaciones, pagos, catálogo y un bus de eventos en el centro.

Nadie decidió eso. Salió de preguntar cómo estructurar el proyecto y aceptar la primera respuesta, que casi siempre son microservicios, porque es lo que más abunda en la literatura técnica de los últimos años.

Los microservicios no son el error. El error es adoptar por moda algo que existe para resolver un problema de equipos, en un proyecto que todavía no tiene equipos.

Qué se está decidiendo en realidad

Las tres opciones son formas de responder a una sola pregunta: dónde pones los límites entre las partes de tu sistema, y qué tan caro es cruzarlos.

El límite y su costo

En un monolito, dos módulos se hablan con una llamada a función: microsegundos, y si falla, falla todo junto de forma predecible.

Entre dos microservicios, esa misma conversación es una petición de red: milisegundos, y puede fallar sola, llegar dos veces o no llegar nunca.

Dividir no elimina la complejidad, la mueve. Sale del código y entra en la red, en el despliegue y en la observabilidad. Esa mudanza se justifica cuando ganas algo a cambio, y no se justifica cuando el problema que resolvía no lo tenías.

Dónde pone cada opción los límites, y qué cuesta cruzarlos

MonolitoMonolito modularMicroservicios
DespliegueUna unidadUna unidadUno por servicio
Comunicación internaLlamada a funciónLlamada a función, por interfazRed
Base de datosUna, compartidaUna, con esquemas separadosUna por servicio
TransaccionesNativasNativasDistribuidas, a mano
Equipos que soporta1 a 8 personas1 a 20 personasVarios equipos autónomos
Depurar un falloUna trazaUna trazaTrazas correlacionadas
Escalar una parte solaNoNoSí
Costo de revertirBajoBajoAlto
Tres columnas comparadas. El monolito es una caja única sin separaciones, donde cruzar un límite no cuesta nada y todo falla junto. El monolito modular es una caja con tres módulos separados, donde cruzar cuesta una llamada de función con contrato y también falla todo junto. Los microservicios son tres cajas sueltas, donde cruzar cuesta una llamada por la red y falla solo una parte, lo que hay que prever
Lo que se decide no es el orden del código, sino si los límites cruzan la red.

La pregunta que ordena la decisión

No es cuántos usuarios tienes ni cuánto tráfico esperas. Es cuántas personas tocan el código a la vez.

Los microservicios resuelven un problema organizativo: varios equipos que se pisan al desplegar, que dependen unos de otros para sacar una funcionalidad, que no pueden moverse a ritmos distintos. Separar los servicios les devuelve autonomía.

Si trabajas solo, o son dos o tres personas, ese problema no existe. Lo que obtienes al dividir es toda la complejidad operativa del reparto sin ninguna de las ventajas organizativas.

Los microservicios son una solución a un problema de equipos, no de código.

El costo que no aparece en la propuesta

Cuando la IA propone una arquitectura de servicios, propone las cajas y las flechas. Lo que rara vez incluye es lo que hay que construir para que esas flechas funcionen.

La diferencia se ve mejor en código. Esta es una operación cualquiera dentro de un proceso:

// Monolito: si algo falla, la transacción entera se revierte sola
$db->beginTransaction();
$pedido = $pedidos->crear($carrito);
$inventario->descontar($carrito->items);   // misma base, mismo proceso
$db->commit();

Y esta es la misma operación cuando pedidos e inventario son servicios separados:

$pedido = $pedidos->crear($carrito);

try {
    // Otro proceso, otra base, otra red
    $inventario->descontar($carrito->items, $pedido->id);
} catch (TimeoutException $e) {
    // ¿Se descontó y se perdió la respuesta, o no se descontó?
    // No hay forma de saberlo desde aquí: hay que preguntar o reintentar
    // con una clave que evite descontar dos veces.
    $compensacion->encolar($pedido->id);
    throw new PedidoIncompletoException();
}

Ese catch es la arquitectura real. Un tiempo de espera agotado no te dice si la operación ocurrió: solo dice que no llegó la respuesta. Para resolverlo necesitas reintentos con una clave de idempotencia, una cola de compensación, y un lugar donde el pedido quede marcado como incompleto hasta que se resuelva.

Multiplica eso por cada par de servicios que se hablan. Es trabajo que en un monolito hace la base de datos con una línea.

El monolito modular: la opción que casi nunca proponen

Hay una tercera vía que rara vez aparece cuando le pides una arquitectura a un modelo, y que para la mayoría de los proyectos es la respuesta correcta: un solo desplegable, con límites internos tratados en serio.

src/
  Pedidos/
    Dominio/          # la lógica, sin saber de HTTP ni de base de datos
    Infraestructura/  # el acceso a datos de este módulo y nada más
    API.php           # lo único que otros módulos pueden llamar
  Inventario/
    Dominio/
    Infraestructura/
    API.php
  Facturacion/
    ...

La regla que lo sostiene es una sola: un módulo se comunica con otro únicamente a través de su archivo público, nunca accediendo a sus tablas ni a sus clases internas. Si Facturación necesita un dato de Pedidos, se lo pide a Pedidos.

Eso conserva lo barato del monolito (una transacción, una traza, un despliegue) y a la vez deja el sistema listo para dividirse si algún día hace falta, porque los límites ya están donde tendrían que estar.

Lo que hace difícil el monolito modular no es técnico, es de disciplina: nada impide saltarse la regla, y una consulta directa a la tabla de otro módulo funciona perfectamente hoy y borra el límite para siempre.

Qué pasa con los datos cuando divides

Es la consecuencia que menos se anticipa y la que más duele. Si cada servicio tiene su propia base, como manda el patrón, entonces las consultas que cruzan áreas dejan de existir.

Un informe tan simple como ventas del mes por categoría de producto es un JOIN entre dos tablas cuando todo vive junto. Con pedidos e inventario separados, ese informe pasa a ser dos llamadas de red y un cruce hecho a mano en memoria, con paginación propia y sin poder ordenar por un campo del otro servicio.

El atajo que anula la división

Ante ese problema, la salida rápida es dejar que el servicio de informes lea directamente las tablas de los demás. Funciona, y es exactamente lo que convierte la arquitectura en un monolito distribuido: varios despliegues acoplados por la base de datos, con toda la latencia de la red y ninguna de las ventajas del aislamiento.

La señal de que pasó esto es concreta: un cambio de esquema en un servicio rompe otro servicio distinto.

Las salidas legítimas son tres, y ninguna es gratis: duplicar los datos que el otro servicio necesita y mantenerlos sincronizados por eventos, construir una vista de solo lectura alimentada por esos eventos, o aceptar que ese informe se arma con varias llamadas y va más lento.

Conviene decidirlo antes de dividir, porque es trabajo que aparece entero el día que alguien pide el primer informe que cruza dos áreas.

El camino intermedio que sí funciona

Casi ningún proyecto pasa de monolito a microservicios de una vez, y los que lo intentan suelen quedarse a mitad. Lo que funciona es sacar un solo servicio, el que tenga un motivo claro para salir.

Ese motivo suele ser uno de tres: consume recursos muy distintos al resto, tiene requisitos de cumplimiento propios, o cambia a un ritmo mucho mayor que el resto del sistema.

El orden que reduce el riesgo es este:

  1. Traza el límite dentro del monolito primero. Que ese módulo ya se comunique solo por su interfaz pública y no comparta tablas con nadie.
  2. Déjalo funcionando así un tiempo. Si aparecen atajos hacia sus tablas, el límite estaba mal puesto y todavía es barato corregirlo.
  3. Recién entonces sácalo. La interfaz ya existe; lo que cambia es que detrás hay una llamada de red y hay que resolver los fallos que eso introduce.

Hacerlo en este orden tiene una ventaja que se nota al final: si en el paso dos descubres que el límite no se sostiene, no desplegaste nada, no montaste infraestructura nueva y no hay nada que revertir.

Tres señales de que dividir sí se justifica

No son de código, y las tres tienen que ver con personas o con dinero.

  • Los despliegues se bloquean entre sí. Varias personas esperan para sacar cambios porque todo sale junto y un fallo en cualquier parte detiene el resto.
  • Una parte tiene necesidades de escala muy distintas. El procesamiento de imágenes necesita diez veces más memoria que el resto, y estás pagando esa memoria para todo el sistema.
  • Una parte tiene requisitos de cumplimiento propios. El módulo que toca datos de pago necesita aislamiento, auditoría separada y acceso restringido.

Si ninguna de las tres aplica, dividir es adelantar un costo cierto a cambio de un beneficio hipotético.

Lo que dividir no arregla

Hay una creencia extendida de que los microservicios ordenan un código desordenado. No lo hacen.

Si los límites entre las partes están mal trazados, separarlas en servicios convierte un acoplamiento incómodo en un acoplamiento distribuido, que es peor: ahora cada llamada entre piezas mal separadas cruza la red, puede fallar sola y hay que versionarla.

El orden viene de definir bien los límites, y eso se puede hacer dentro de un monolito, gratis. Si no eres capaz de trazarlos ahí, tampoco los vas a trazar bien repartidos en servicios.

Todo el sistema es un solo desplegable, con una base de datos y un proceso.

Ventaja: es lo más simple de operar. Una traza cuando algo falla, transacciones nativas, un despliegue, y cambiar algo que cruza dos áreas es un solo cambio.

Costo: a medida que crece, los límites internos se borran si nadie los cuida, y todo el equipo depende del mismo despliegue.

Úsalo si son pocas personas y el sistema cabe en tu cabeza. Evítalo cuando varios equipos se bloquean entre sí para sacar cambios.

Un solo desplegable, pero con módulos que solo se hablan a través de interfaces públicas y no comparten tablas entre sí.

Ventaja: conserva las transacciones, la traza única y el despliegue simple, y a la vez deja los límites trazados por si algún día hay que dividir.

Costo: depende de disciplina. Nada impide saltarse la regla, y un atajo hacia la tabla de otro módulo borra el límite en silencio.

Úsalo como opción por defecto en casi cualquier proyecto que espere crecer. Evítalo solo si ya tienes equipos que necesitan desplegar por separado.

Cada parte es un servicio con su propio despliegue, su propia base de datos y su propio ciclo de vida.

Ventaja: equipos autónomos que despliegan sin coordinarse, y la posibilidad de escalar o aislar una parte sin tocar las demás.

Costo: las transacciones dejan de ser nativas, cada llamada puede fallar sola, y necesitas trazas correlacionadas para saber qué pasó.

Úsalo cuando el cuello de botella es organizativo o hay requisitos de aislamiento reales. Evítalo mientras el equipo quepa en una sala.

Cómo decidir sin arrepentirse

El camino que casi siempre sale bien es el mismo que casi nunca se propone: empezar por un monolito modular y dividir solo la pieza que lo pida, cuando lo pida.

Dividir después es un trabajo acotado si los límites ya estaban trazados: sacas un módulo, le pones una interfaz de red delante y lo despliegas aparte. Unir servicios que nunca debieron separarse es mucho más caro, y por eso casi nadie lo hace, aunque le convendría.

Tres preguntas que ordenan la decisión, en este orden:

  1. ¿Cuántas personas despliegan? Si la respuesta es una o dos, no hay caso que justifique microservicios.
  2. ¿Hay una parte con escala o cumplimiento distintos? Esa es la primera candidata a salir, y normalmente la única.
  3. ¿Los límites internos están claros hoy? Si no lo están, arréglalos dentro del monolito antes de repartirlos por la red.

Y una advertencia sobre lo que vas a recibir si preguntas sin contexto: la respuesta por defecto tiende a ser la arquitectura más completa, no la más adecuada. Decir somos dos personas y un hosting compartido cambia la propuesta por completo.


Comparativa intermedio