Lección 1 de 915 min
Del monolito a los microservicios
Monolito, monolito modular, microservicios, SOA, serverless, arquitecturas orientadas a eventos y microkernel.
Imagina una tienda online que empezó con tres personas y un solo proyecto. Dos años después hay sesenta desarrolladores, cada despliegue exige coordinar a cinco equipos y un fallo en las recomendaciones tumba también el carrito. ¿Hay que partir el sistema? ¿Por dónde? ¿Y qué se gana y qué se pierde al hacerlo?
La arquitectura de sistema responde a esas preguntas: cómo se reparte una aplicación en piezas que se despliegan, escalan y fallan juntas o por separado. Ninguna opción es mejor que las demás en todo; cada una cambia unos problemas por otros. Aquí verás las siete más habituales y cuándo encaja cada una.
Monolitos y servicios
La primera gran decisión es cuántas unidades despliegas. En un extremo, una sola aplicación; en el otro, decenas de servicios pequeños que se hablan por la red.
Monolito
Toda la aplicación en un solo proyecto que se despliega como una única unidad.
- Despliegue:
- Una sola unidad
- Datos:
- Normalmente, una base compartida
En un monolito, la interfaz, la lógica de negocio y el acceso a datos viven en el mismo código y se ejecutan en el mismo proceso. Las piezas se llaman entre sí con simples llamadas a funciones y suelen compartir una base de datos, así que una operación que toca pedidos, stock y facturas cabe en una sola transacción.
Para escalar, ejecutas varias copias idénticas detrás de un balanceador de carga. Es sencillo de desarrollar, probar y depurar: un repositorio, un despliegue y una sola pila de llamadas cuando algo falla.
Úsalo cuando
Al empezar un producto, con un equipo pequeño o cuando aún no sabes bien dónde están las fronteras del negocio. Muchas empresas grandes funcionan con monolitos bien cuidados.
Ten en cuenta
Sin disciplina degenera en una «gran bola de barro» en la que todo depende de todo. Además, una fuga de memoria en una parte tumba la aplicación entera, y no puedes escalar solo la zona que más carga recibe.
// Todo vive en el mismo proceso: son llamadas a funciones
export async function crearPedido(usuarioId: string, carrito: Carrito) {
return db.transaction(async (tx) => {
await inventario.reservar(tx, carrito.lineas);
const factura = await facturas.emitir(tx, usuarioId, carrito.total);
return pedidos.guardar(tx, { usuarioId, carrito, facturaId: factura.id });
});
// Si algo falla, la transacción deshace los tres pasos
}Monolito modular
Un solo despliegue, pero dividido por dentro en módulos con fronteras estrictas.
- Despliegue:
- Una sola unidad
- Fronteras:
- Módulos con API pública
Un monolito modular se despliega como un monolito, pero su código se organiza en módulos de negocio (catálogo, pedidos, pagos) que solo se comunican a través de una interfaz pública. Ningún módulo lee las tablas ni importa los archivos internos de otro.
Esas fronteras se hacen cumplir con herramientas: reglas de lint que prohíben ciertos imports, paquetes separados en un monorepo o un esquema de base de datos por módulo. Si un día un módulo necesita escalar aparte, extraerlo como servicio es mucho más fácil, porque ya tiene una frontera limpia.
Úsalo cuando
Cuando el equipo crece y quieres orden y autonomía entre áreas sin pagar el coste operativo de un sistema distribuido. Para muchos productos es el punto de equilibrio.
Ten en cuenta
Las fronteras solo existen si algo las vigila: sin reglas automáticas, la prisa acaba colando un import «solo esta vez». Y el despliegue sigue siendo compartido: un error en un módulo puede frenar la salida a producción de los demás.
// Bien: usa la API pública del módulo de pagos
import { emitirFactura } from "@/modulos/pagos";
// Mal: entra en las tripas de otro módulo.
// Una regla de lint debería rechazar este import.
import { tablaFacturas } from "@/modulos/pagos/interno/db";
export async function crearPedido(datos: DatosPedido) {
const pedido = await guardarPedido(datos);
await emitirFactura(pedido.id, pedido.total);
return pedido;
}Microservicios
Muchos servicios pequeños, cada uno con su código, sus datos y su despliegue.
- Despliegue:
- Independiente por servicio
- Datos:
- Una base por servicio
En una arquitectura de microservicios, cada capacidad de negocio es un servicio independiente: tiene su código, su base de datos y su propio ciclo de despliegue, y lo mantiene un equipo. Los servicios se comunican por la red, con HTTP, gRPC o mensajes.
La gran ventaja es la autonomía: el equipo de pagos despliega varias veces al día sin coordinarse con nadie, y el catálogo puede escalar a cincuenta copias mientras el resto sigue con dos. El precio es que ahora tienes un sistema distribuido: la red falla, las llamadas son más lentas y ya no existe una transacción que lo abarque todo.
Úsalo cuando
Con muchos equipos que necesitan desplegar por separado, partes del sistema con necesidades de escala muy distintas y la madurez operativa para sostenerlo: CI/CD, contenedores y observabilidad.
Ten en cuenta
Si los servicios tienen que desplegarse juntos o comparten base de datos, tienes un «monolito distribuido»: lo peor de los dos mundos. Necesitarás timeouts, reintentos y sagas, que verás en la lección de patrones de resiliencia.
// El inventario es otro servicio: hay que llamarlo por la red
export async function crearPedido(datos: DatosPedido) {
const respuesta = await fetch("http://inventario/reservas", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ lineas: datos.lineas }),
signal: AbortSignal.timeout(2000), // la red puede no responder
});
if (!respuesta.ok) throw new Error("No se pudo reservar el stock");
return pedidosDb.guardar(datos); // base de datos propia de este servicio
}SOA (arquitectura orientada a servicios)
Servicios reutilizables a escala de empresa, a menudo conectados por un bus central.
- Pieza típica:
- ESB
- Contratos:
- A menudo SOAP y WSDL
SOA (Service-Oriented Architecture) se popularizó en los años 2000 para que las grandes organizaciones reutilizaran funciones entre aplicaciones: un servicio «Clientes» o «Facturación» que consumen a la vez el CRM, la web y la contabilidad. Los contratos suelen ser formales, a menudo SOAP descrito con WSDL.
Una pieza típica es el ESB (Enterprise Service Bus), un intermediario que enruta los mensajes, transforma formatos y orquesta procesos. Los microservicios heredan la idea de servicio, pero rechazan el bus central: prefieren, como suele decirse, «extremos inteligentes y tuberías simples».
Úsalo cuando
En integraciones empresariales con muchos sistemas heterogéneos (mainframes, ERP, aplicaciones compradas) que deben compartir servicios y contratos comunes.
Ten en cuenta
El ESB tiende a convertirse en un cuello de botella y en un punto único de fallo, y acumula lógica de negocio que nadie quiere tocar. Para un producto nuevo, rara vez es la opción natural.
// Contrato del servicio corporativo «Clientes». En SOA suele
// publicarse como WSDL y lo consumen varias aplicaciones a la vez.
export interface ServicioClientes {
consultarCliente(id: string): Promise<Cliente>;
actualizarDireccion(id: string, direccion: Direccion): Promise<void>;
}
// El CRM, la tienda online y la contabilidad lo llaman a través
// del bus (ESB), que traduce formatos y enruta cada mensaje.La ley de Conway
En 1968, Melvin Conway observó que las organizaciones diseñan sistemas que copian su estructura de comunicación. Si tienes tres equipos que apenas hablan entre sí, es probable que acabes con tres piezas separadas, lo hayas planeado o no. Por eso la arquitectura y la organización de los equipos se deciden juntas.
Otras formas de repartir el trabajo
No todo se reduce a «uno o muchos servicios». Estas tres arquitecturas cambian otra cosa: quién gestiona los servidores, cómo se comunican las piezas o cómo se amplía un producto.
Serverless
Funciones que se ejecutan bajo demanda; el proveedor gestiona servidores y escalado.
- Ejemplos:
- AWS Lambda, Azure Functions, Cloudflare Workers
- Cobro:
- Por uso
Con serverless escribes funciones pequeñas que se ejecutan cuando ocurre algo: una petición HTTP, un archivo subido, un mensaje en una cola. El proveedor de cloud arranca tantas instancias como haga falta, las apaga cuando no hay trabajo y te cobra por uso. Los servidores siguen existiendo, pero no son asunto tuyo.
A este modelo también se le llama FaaS (Functions as a Service). A menudo se combina con servicios gestionados, como bases de datos o colas, para no administrar nada de infraestructura.
Úsalo cuando
Para tráfico irregular o con picos, tareas en segundo plano (procesar imágenes, enviar correos) y APIs pequeñas en las que no quieres mantener servidores.
Ten en cuenta
El arranque en frío (cold start) añade latencia cuando la plataforma tiene que crear una instancia nueva. Las funciones tienen límites de duración y memoria, no conservan estado entre ejecuciones y, con tráfico alto y constante, pueden salir más caras que un servidor propio. Lo verás en el módulo de cloud.
// Se ejecuta cada vez que alguien sube imágenes al almacenamiento
export async function handler(evento: EventoSubida) {
for (const archivo of evento.archivos) {
const original = await almacenamiento.leer(archivo.ruta);
const miniatura = await redimensionar(original, { ancho: 320 });
await almacenamiento.guardar(`miniaturas/${archivo.nombre}`, miniatura);
}
// No hay servidor que mantener: si llegan mil subidas,
// la plataforma ejecuta muchas copias en paralelo
}Arquitectura orientada a eventos
Las piezas se avisan publicando hechos («ha pasado X») en vez de llamarse entre sí.
- Brokers:
- Kafka, RabbitMQ, colas cloud
- Consistencia:
- Eventual
En una arquitectura orientada a eventos (EDA, Event-Driven Architecture), un servicio que hace algo importante publica un evento: «PedidoCreado», «PagoRechazado». Otros servicios se suscriben a los eventos que les interesan y reaccionan por su cuenta. Quien publica no sabe quién escucha.
Un intermediario, el broker de mensajes (Kafka o RabbitMQ, por ejemplo), recibe los eventos y los entrega. Así puedes añadir un consumidor nuevo, como uno que calcula estadísticas, sin tocar el servicio de pedidos. El módulo de caché, colas y mensajería explica estos brokers a fondo.
Úsalo cuando
Cuando una acción desencadena varias reacciones independientes, cuando quieres desacoplar equipos o cuando el trabajo puede hacerse en diferido: notificaciones, analítica, integraciones.
Ten en cuenta
El flujo deja de leerse de arriba abajo: para saber qué pasa tras un pedido hay que rastrear quién consume cada evento. Hay que asumir consistencia eventual (los datos tardan un poco en coincidir en todas partes), mensajes duplicados o desordenados y consumidores que lo toleren.
// Pedidos publica un hecho y sigue con lo suyo
await broker.publicar("PedidoCreado", { pedidoId: "p-81", total: 59.9 });
// Cada servicio reacciona por su cuenta, sin que Pedidos lo sepa
broker.suscribir("PedidoCreado", async (evento) => {
await correo.enviarConfirmacion(evento.pedidoId);
});
broker.suscribir("PedidoCreado", async (evento) => {
await almacen.prepararEnvio(evento.pedidoId);
});Microkernel (arquitectura de plugins)
Un núcleo mínimo y estable que se amplía con plugins que se enchufan a él.
- Ejemplos:
- VS Code, ESLint, Vite
La arquitectura microkernel separa un núcleo pequeño, con lo imprescindible, de un conjunto de plugins que añaden funciones. El núcleo define puntos de extensión (una interfaz que cada plugin implementa) y se encarga de registrarlos y llamarlos en el momento adecuado.
La usas a diario: VS Code se amplía con extensiones, los navegadores con complementos, y herramientas como ESLint, Vite o Babel hacen gran parte de su trabajo mediante plugins. El nombre viene de los sistemas operativos, aunque aquí se aplica a una sola aplicación.
Úsalo cuando
En productos que terceros deben poder ampliar, o cuando las variaciones (formatos, reglas por cliente, integraciones) cambian mucho más que el núcleo.
Ten en cuenta
Diseñar bien la interfaz de plugins es difícil y, una vez publicada, cambiarla rompe todo el ecosistema. Además, un plugin mal escrito puede ralentizar o romper el núcleo si no lo aíslas.
interface Plugin {
nombre: string;
transformar(codigo: string): string;
}
class Compilador {
private plugins: Plugin[] = [];
usar(plugin: Plugin) {
this.plugins.push(plugin);
}
compilar(codigo: string) {
// El núcleo no sabe qué hace cada plugin: solo los encadena
return this.plugins.reduce((resultado, p) => p.transformar(resultado), codigo);
}
}Se pueden combinar
Estas arquitecturas no se excluyen. Un monolito modular puede publicar eventos, delegar el procesado de imágenes en funciones serverless y admitir plugins. Lo habitual es elegir lo que necesita cada parte del sistema y empezar por lo más simple que funcione.
Interactivo
Siete arquitecturas cara a cara
Activa o desactiva opciones para compararlas cara a cara.
| Característica | Monolito | Monolito modular | Microservicios |
|---|---|---|---|
| Despliegue | Una unidad | Una unidad | Independiente por servicio |
| Escalado | Copias de la aplicación entera | Copias de la aplicación entera | Cada servicio por separado |
| Tamaño de equipo | Pequeño o mediano | Mediano, varios equipos | Grande, muchos equipos |
| Complejidad operativa | Baja | Baja | Alta |
| Uso típico | Productos nuevos, equipos pequeños | Productos que crecen sin querer distribuirse | Plataformas grandes con muchos equipos |
Ponlo en práctica
Saber qué es cada cosa es la mitad del trabajo. La otra mitad es reconocer cuándo usarla.
Interactivo
¿Qué usarías?
Lee cada situación y elige la opción que mejor la resuelve. Fallar aquí no cuenta: es para practicar.
Con un equipo de cuatro personas, vas a lanzar un producto mínimo de reservas para peluquerías. Aún no sabes qué funciones sobrevivirán dentro de seis meses.
Para recordar
- Cada arquitectura cambia unos problemas por otros: los microservicios dan autonomía y escalado independiente a cambio de la complejidad de un sistema distribuido.
- Un monolito bien modularizado es una opción seria, no un paso vergonzoso: muchos productos nunca necesitan más.
- Serverless delega los servidores en el proveedor; la arquitectura orientada a eventos separa a quien hace algo de quien reacciona.
- Microkernel separa un núcleo estable de los plugins que lo amplían: es la arquitectura de editores y herramientas de build.
- La forma de los equipos acaba reflejándose en la del sistema (ley de Conway): decide ambas a la vez.
Comprueba lo aprendido
Cuestionario
Responde las 5 preguntas para completar la lección.
1.¿Qué es un «monolito distribuido»?
2.¿Qué diferencia a un monolito modular de un monolito sin más?
3.En una función serverless, ¿qué es el «arranque en frío»?
4.En una arquitectura orientada a eventos, el servicio de pedidos publica «PedidoCreado». ¿Qué sabe de quién lo consume?
5.Un equipo de tres personas quiere empezar con microservicios «porque escalan mejor». ¿Cuál es la objeción principal?