Monolito vs Microservicios

Comparación entre arquitectura monolítica y microservicios: ventajas, desventajas, cuándo usar cada una y consideraciones de migración.

Dos enfoques arquitectónicos distintos

Cuando diseñas un sistema, una de las primeras decisiones es cómo organizar el código y los procesos. Los dos extremos del espectro son la arquitectura monolítica y los microservicios. Ninguno es inherentemente mejor; cada uno resuelve problemas diferentes en contextos diferentes.

¿Qué es un monolito?

Un monolito es una aplicación que agrupa múltiples capacidades del sistema dentro de una única unidad desplegable.

Esto no significa necesariamente que esté mal diseñado. Un monolito puede estar bien estructurado internamente, con módulos, capas y límites claros. Lo importante es que, desde el punto de vista operativo, se despliega como una sola aplicación.

Estructura típica

mi-aplicacion/
├── src/
│   ├── controllers/    # Endpoints HTTP
│   ├── services/       # Lógica de negocio
│   ├── models/         # Modelos de datos
│   ├── repositories/   # Acceso a base de datos
│   └── utils/          # Utilidades compartidas
├── database/
│   └── migrations/
└── package.json

¿Qué son los microservicios?

Los microservicios separan el sistema en servicios más pequeños, cada uno responsable de un dominio o capacidad concreta.

Cada servicio suele tener:

  • Su propia lógica de negocio
  • Su despliegue independiente
  • Su persistencia o acceso a datos propio
  • Sus contratos de entrada y salida

La idea no es “hacer servicios pequeños porque sí”, sino crear límites que permitan evolucionar partes del sistema con menor acoplamiento.

Estructura típica

sistema/
├── servicio-usuarios/
│   ├── src/
│   ├── Dockerfile
│   └── package.json
├── servicio-pedidos/
│   ├── src/
│   ├── Dockerfile
│   └── package.json
├── servicio-pagos/
│   ├── src/
│   ├── Dockerfile
│   └── package.json
└── api-gateway/
    ├── src/
    └── package.json

Ventajas del monolito

Simplicidad inicial

Un solo proyecto suele ser más fácil de arrancar, desplegar y depurar. No necesitas resolver desde el día uno temas como mensajería, trazabilidad distribuida o despliegues coordinados.

Menor complejidad operativa

No necesitas orquestación de contenedores, service discovery, monitoreo distribuido ni gestión de consistencia eventual desde el principio.

Más fácil para equipos pequeños

Cuando el equipo es reducido (2-8 personas), muchas veces resulta más eficiente trabajar sobre una única base de código bien organizada.

Transacciones ACID

Las operaciones de base de datos son simples porque todo comparte la misma conexión. No necesitas patrones como Saga o Outbox para mantener consistencia.

Más sencillo de testear end-to-end

La integración entre módulos ocurre en memoria o en un mismo proceso, lo que reduce fricción al probar flujos completos.

Menor latencia interna

Las llamadas entre módulos son llamadas a funciones en memoria, no llamadas de red. Esto elimina la latencia inherente a la comunicación entre servicios.

Desventajas del monolito

Acoplamiento creciente

Si no hay disciplina interna, el sistema puede volverse difícil de mantener. Con el tiempo, los módulos tienden a entrelazarse y las dependencias se vuelven difíciles de rastrear.

Despliegue único

Cambiar una parte obliga a desplegar toda la aplicación. Un cambio en un módulo requiere redesplegar todo el sistema, lo que aumenta el riesgo.

Escalado menos granular

No puedes escalar solo la parte que más carga recibe. Debes escalar toda la aplicación aunque solo un módulo necesite más recursos.

Menor autonomía organizacional

Con varios equipos, coordinar cambios puede volverse costoso. Todo el equipo debe usar el mismo lenguaje y framework.

Ventajas de los microservicios

Separación por dominios

Permite aislar capacidades del negocio y sus reglas. Cada servicio encapsula un bounded context con su propio modelo de datos.

Despliegues independientes

Cada servicio puede evolucionar a su ritmo sin afectar a los demás. Un equipo puede desplegar su servicio sin coordinar con otros equipos.

Escalabilidad más granular

Puedes escalar solo lo que realmente lo necesita. Si el servicio de búsqueda recibe 10x más tráfico que el de facturación, solo escalas búsqueda.

Equipos más autónomos

Cada equipo puede responsabilizarse de un bounded context o dominio completo, tomando decisiones técnicas de forma independiente.

Aislamiento de fallos

Si un servicio cae, los demás pueden seguir funcionando (con diseño adecuado). Esto mejora la resiliencia general del sistema.

Flexibilidad tecnológica

En ciertos casos, permite elegir tecnologías distintas por servicio. Un servicio puede usar Node.js mientras otro usa Go, según las necesidades.

Desventajas de los microservicios

Complejidad operativa

Aparecen problemas que en un monolito no existen:

  • Descubrimiento de servicios
  • Observabilidad distribuida
  • Reintentos e idempotencia
  • Fallos de red
  • Versionado de contratos
  • Orquestación de contenedores

Latencia de red

Cada llamada entre servicios pasa por la red, lo que añade latencia. Un flujo que en un monolito es una llamada a función, en microservicios puede involucrar múltiples saltos de red.

Consistencia más compleja

Las transacciones distribuidas no son triviales. Debes aceptar consistencia eventual en muchos casos y usar patrones como Saga para coordinar operaciones entre servicios.

Depuración difícil

Rastrear un error a través de múltiples servicios requiere herramientas de tracing distribuido como Jaeger o Zipkin. Sin ellas, diagnosticar problemas es extremadamente difícil.

Más esfuerzo de gobernanza

Hay que definir estándares técnicos, ownership y reglas de integración. Sin gobernanza, cada equipo puede tomar decisiones incompatibles.

Duplicación de código

Lógica común puede terminar duplicada entre servicios si no se gestiona adecuadamente con shared kernels o librerías compartidas.

Más infraestructura

Necesitas pipelines de CI/CD, despliegue automatizado, monitoreo y operación más maduros desde el inicio.

Comparación directa

CriterioMonolitoMicroservicios
Complejidad inicialBajaAlta
EscalabilidadVertical (más recursos)Horizontal (más instancias)
DespliegueTodo juntoIndependiente por servicio
Consistencia de datosACID nativoConsistencia eventual
Tamaño de equipo ideal2-8 personasMúltiples equipos (8+)
Time to market inicialRápidoLento
Costo operacionalBajoAlto
Autonomía de equiposLimitadaAlta
Aislamiento de fallosBajoAlto
Flexibilidad tecnológicaUna sola stackMúltiples stacks posibles

¿Cuál es mejor?

La pregunta correcta no es “qué es mejor en general”, sino:

  • ¿Cuál resuelve mejor mis necesidades actuales?
  • ¿Cuál puedo operar con el equipo que tengo?
  • ¿Qué nivel de complejidad de negocio manejo?
  • ¿Necesito despliegues realmente independientes?
  • ¿Tengo la madurez técnica para sostenerlo?

Elige monolito cuando

  • Estás comenzando un proyecto nuevo y el dominio no está completamente definido.
  • Tu equipo es pequeño (menos de 8 personas).
  • Necesitas llegar al mercado rápidamente (MVP).
  • El sistema no requiere escalado diferenciado por módulo.
  • No tienes experiencia operando infraestructura distribuida.

Elige microservicios cuando

  • El sistema ha crecido y los equipos se pisan entre sí al hacer cambios.
  • Necesitas escalar módulos específicos de forma independiente.
  • Tienes equipos autónomos que pueden ser dueños de servicios completos.
  • El dominio de negocio tiene límites claros entre contextos (bounded contexts).
  • Cuentas con la infraestructura y experiencia para operar sistemas distribuidos.

El camino intermedio: monolito modular

En muchos casos, un monolito modular es una gran solución inicial. Y, en algunos contextos, puede seguir siéndolo durante mucho tiempo.

Es un monolito con límites internos bien definidos entre módulos:

mi-aplicacion/
├── modules/
│   ├── usuarios/
│   │   ├── controllers/
│   │   ├── services/
│   │   └── models/
│   ├── pedidos/
│   │   ├── controllers/
│   │   ├── services/
│   │   └── models/
│   └── pagos/
│       ├── controllers/
│       ├── services/
│       └── models/
└── shared/
    └── utils/

Cada módulo tiene su propia estructura interna y se comunica con otros módulos a través de interfaces bien definidas. Esto te da muchos beneficios de los microservicios (bajo acoplamiento, alta cohesión) sin la complejidad operacional.

Evolución realista

Un error común es intentar empezar con microservicios demasiado pronto. Eso suele crear más complejidad técnica que valor real.

Una evolución más sana suele ser:

  1. Empezar con un sistema simple
  2. Modularizarlo bien
  3. Identificar dominios y límites reales
  4. Separar solo cuando haya presión real para hacerlo

Es decir: primero entender el dominio, luego decidir la forma arquitectónica.

Patrón Strangler Fig

Si decides migrar de monolito a microservicios, no reescribas todo de golpe. Extrae funcionalidad gradualmente:

  1. Identifica un módulo con límites claros.
  2. Crea un nuevo servicio que replique esa funcionalidad.
  3. Redirige el tráfico al nuevo servicio.
  4. Elimina el código del monolito cuando el servicio esté estable.

Errores comunes en la migración

  • Microservicios demasiado pequeños: Si un servicio no puede funcionar sin llamar constantemente a otros, probablemente es demasiado pequeño.
  • Base de datos compartida: Si dos servicios comparten la misma base de datos, no son realmente independientes.
  • Migrar sin automatización: Sin CI/CD, monitoreo y orquestación, los microservicios se vuelven inmanejables.

Resumen

Monolito y microservicios no son “bueno vs malo”. Son estrategias distintas para organizar un sistema. El monolito suele ofrecer simplicidad inicial; los microservicios ofrecen separación y autonomía a costa de mayor complejidad operativa.

La mejor arquitectura es la que resuelve tu problema actual sin crear problemas futuros innecesarios. Empieza simple, mide, y evoluciona cuando los datos lo justifiquen.