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
| Criterio | Monolito | Microservicios |
|---|---|---|
| Complejidad inicial | Baja | Alta |
| Escalabilidad | Vertical (más recursos) | Horizontal (más instancias) |
| Despliegue | Todo junto | Independiente por servicio |
| Consistencia de datos | ACID nativo | Consistencia eventual |
| Tamaño de equipo ideal | 2-8 personas | Múltiples equipos (8+) |
| Time to market inicial | Rápido | Lento |
| Costo operacional | Bajo | Alto |
| Autonomía de equipos | Limitada | Alta |
| Aislamiento de fallos | Bajo | Alto |
| Flexibilidad tecnológica | Una sola stack | Mú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:
- Empezar con un sistema simple
- Modularizarlo bien
- Identificar dominios y límites reales
- 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:
- Identifica un módulo con límites claros.
- Crea un nuevo servicio que replique esa funcionalidad.
- Redirige el tráfico al nuevo servicio.
- 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.