El otro día estaba platicando con un cliente que quería montar un e-commerce pequeño, algo para vender camisas personalizadas. Lo primero que me dijo fue: “José, ocupo que esto esté en microservicios porque escuché que Netflix y Uber así lo hacen”.
Casi se me cae el café.
Es el mal de nuestra industria: el Hype-Driven Development. Queremos usar herramientas de transnacionales para resolver problemas de pulpería. No me malinterpretes, los microservicios son una maravilla, pero tienen un precio en complejidad (y también en costos) que no todos están dispuestos (o preparados) para pagar.
Hoy vamos a bajar esto a tierra. Si vienes de frameworks como Express en Node, Spring Boot en Java o .NET, seguro has sentido la tentación de separar todo en APIs distintas. Pero antes de que empieces a crear 20 repositorios, hablemos de cuándo sí y cuándo mejor “ahí dejalo”.
El Monolito: No es un insulto, es un aliado
Antes de hablar de microservicios, hay que darle amor al Monolito. Un monolito es una sola unidad de despliegue donde vive toda tu lógica (Usuarios, Ventas, Productos).
Casos donde NO necesitas microservicios:
- Equipos pequeños: Si solo eres tú y otro compa, gestionar 10 microservicios con sus propios pipelines de CI/CD, bases de datos y redes les va a quitar más tiempo que programar la lógica del negocio.
- MVP (Producto Mínimo Viable): Cuando estás validando una idea, ocupas velocidad. El monolito se despliega de un solo golpe y listo.
- Baja complejidad de dominio: Si tus módulos están súper acoplados y siempre cambian juntos, separarlos solo va a crear latencia de red innecesaria.
Tip de Pro: Empieza con un Monolito Modular. Organiza tu código por carpetas bien separadas (usando Domain Driven Design). Si el día de mañana ocupas sacar un módulo a un microservicio, será como quitar una pieza de Lego en lugar de desenredar un plato de espaguetis.
¿Cuándo sí dar el salto? (Los “Red Flags”)
Fíjate bien, llega un punto donde el monolito estorba. Ahí es donde los microservicios se ponen “de lujo” y valen la pena el esfuerzo.
- Escalabilidad selectiva: Imagina un sistema como YouTube. El módulo de Subir Video consume muchísima CPU, pero el de Comentarios consume más red. En un monolito, escalas todo junto. En microservicios, le asignas 50 servidores a la subida de videos y solo 2 a los comentarios.
- Equipos independientes: Cuando la empresa crece y tienes 30 desarrolladores, todos tocando el mismo repositorio es un caos. Los microservicios permiten que un equipo trabaje en Go para el motor de pagos, mientras otro usa Node.js para el chat, sin estorbarse.
Show me the code: Comunicando Servicios
Si decides irte por los microservicios, ya no puedes simplemente importar un archivo y llamar a una función userService.getUser(). Ahora tienes que viajar por la red.
Imagina que tienes un microservicio de Órdenes escrito en Node.js (TypeScript) que necesita consultar al microservicio de Usuarios. Lo haremos mediante una simple petición HTTP.
Primero, instala tu cliente HTTP (en este caso, Axios):
npm install axios
npm install -D @types/axios
Ahora, veamos cómo sería el código implementando una arquitectura limpia con sus modelos de datos bien definidos:
import axios from "axios";
// 1. Definimos los tipos de datos (Contrato)
export interface User {
id: string;
name: string;
email: string;
isActive: boolean;
}
export interface Order {
orderId: string;
amount: number;
userId: string;
}
// 2. Creamos un cliente para comunicarnos con el otro microservicio
export class UserServiceClient {
// En la vida real, esto viene de variables de entorno (.env)
private readonly baseUrl =
process.env.USER_SERVICE_URL || "http://user-service:3000";
async getUserInfo(userId: string): Promise<User> {
try {
const response = await axios.get<User>(`${this.baseUrl}/users/${userId}`);
return response.data;
} catch (error) {
console.error(`Error de red al buscar el usuario ${userId}:`, error);
// Aquí no hacemos un simple throw, manejamos el error de microservicio a microservicio
throw new Error("UserService_Unavailable");
}
}
}
// 3. Uso en tu caso de uso de Órdenes
async function processOrder(order: Order) {
const userClient = new UserServiceClient();
// Llamada de red a otro servicio
const user = await userClient.getUserInfo(order.userId);
if (!user.isActive) {
throw new Error("No puedes procesar órdenes para un usuario inactivo");
}
console.log(`Procesando orden de $${order.amount} para ${user.name}`);
}
Si te fijas, la sección donde las ordenes solicitas información de usuarios ese igual a tener un servicio de usuarios en tu aplicación monolítica, solo que ahora es un servicio externo. Este es el punto importante de arquitectura y modularización de los componentes aun en monolitos. Si lo haces bien puedes migrar a microservicios sin problemas.
Sin embargo, debes aceptar que tu petición ahora es mas lenta por la latencia de red. Al menos más lenta que antes. Es un costo que debes pagar por los beneficios de los microservicios. Existen formas de reducir esta latencia, como el uso de GraphQL o gRPC, pero nunca será igual que tener todo en un solo servicio. Eso es tema para otro artículo.
Dato para Ingenieros: Al usar microservicios, el manejo de errores se vuelve crítico. Si el servicio de Usuarios se cae, el servicio de Órdenes también va a fallar. Para evitar este efecto dominó, debes implementar patrones de resiliencia como Circuit Breaker (Cortocircuito) o usar comunicación asíncrona con un Message Broker (como RabbitMQ o Kafka).
Para terminar
Los microservicios no son un objetivo final, son una solución a un problema de escala y organización humana. Si tu aplicación apenas arranca y tu equipo es pequeño, quédate con un monolito bien estructurado. Te ahorrarás meses de dolores de cabeza configurando redes, Docker y Kubernetes.
Pero si notas que tus despliegues son lentos, el código es inmanejable y tienes cuellos de botella de rendimiento en funciones específicas, entonces es hora de romper el cascarón y distribuir tu sistema.
¿Y tú qué opinas? ¿Has tenido pesadillas tratando de orquestar microservicios o eres del Team Monolito a muerte? ¡Contame en los comentarios!