Microservicios: la trampa del monolito distribuido
Dividir tu aplicación en 10 servicios no la hace más rápida. Hace que sea más difícil de depurar. Una guía técnica sobre monolitos modulares, diseño basado en dominios (DDD) y patrón Saga.
En 2015, el mantra de la industria era absoluto: “Los monolitos son dinosaurios. Los microservicios son el futuro”. Para 2025, la resaca ya habrá llegado. Las empresas que dividieron ciegamente sus aplicaciones ahora se están ahogando en lo que llamamos el Monolito Distribuido: un sistema con toda la complejidad de la computación distribuida y ninguno de los beneficios del desacoplamiento.
En Maison Code Paris, actuamos como la conciencia arquitectónica de nuestros clientes. A menudo heredamos proyectos de “Microservicios” en los que un simple inicio de sesión de usuario afecta a seis servicios diferentes, tiene una latencia de 2 segundos y cuesta 5.000 € al mes en tarifas de acceso a la nube.
Este artículo es una guía técnica y sobria sobre cuándo adoptar microservicios y, lo que es más importante, cuándo huir de ellos.
Por qué Maison Code habla de esto
En Maison Code Paris, actuamos como la conciencia arquitectónica de nuestros clientes. A menudo heredamos stacks “modernos” construidos sin una comprensión fundamental de la escala.
Discutimos este tema porque representa un punto de inflexión crítico en la madurez de la ingeniería. Implementarlo correctamente diferencia un MVP frágil de una plataforma resistente de nivel empresarial.
El caso del monolito modular
Antes de escribir mkdir service-user, considere el Modular Monolith.
Se trata de una única unidad desplegable (un binario/contenedor) donde el código está estrictamente separado en módulos (paquetes) que reflejan los dominios empresariales.
La estructura importa
Un “Monolito de espagueti” tiene archivos que se importan entre sí de forma aleatoria. Un monolito modular tiene límites estrictos impuestos por reglas de linting u objetivos de compilación.
src/
módulos/
auth/ # Contexto acotado: Autenticación
api/# Interfaz pública
interno/# Implementación privada (base de datos, lógica)
inventario/# Contexto acotado: Inventario
API/
interno/
facturación/# Contexto acotado: Facturación
API/
interno/
La Ley de Demeter: El código en facturación no puede importar inventario/interno. Solo puede llamar a “inventario/api”.
Si sigue esta validación, obtendrá el 90% de los beneficios de los microservicios (separación de equipos, código más limpio) con el 0% de los inconvenientes (latencia de red, coherencia eventual, complejidad de implementación).
Cuándo dividir (las condiciones límite)
Solo recomendamos dividir un módulo en un microservicio separado si cumple uno de estos estrictos criterios:
-
Requisitos de hardware heterogéneos:
- La API principal está vinculada a E/S. Necesita 1 GB de RAM y 0,5 CPU.
- El Procesador de imágenes está vinculado a la CPU. Necesita 16 GB de RAM y acceso a GPU.
- Decisión: Extraer el Procesador de Imágenes. Implementar nodos GPU para la API es una pérdida de dinero.
-
Velocidad de liberación independiente:
- El Equipo de pago envía el código cada hora.
- El Equipo de contabilidad (Contabilidad) trabaja una vez al mes después de una auditoría rigurosa.
- Decisión: Extraer Checkout para evitar bloquear al equipo de alta velocidad.
-
Aislamiento fatal (el “radio de explosión”):
- Si el Generador de PDF tiene una pérdida de memoria y falla, no debería desactivar el Pasarela de pago.
- Decisión: Aislar componentes inestables o riesgosos.
Patrones de comunicación: REST frente a gRPC frente a eventos
Una vez que tienes múltiples servicios, el problema más difícil es la comunicación. Las “llamadas a funciones” se convierten en “paquetes de red”.
Síncrono: la trampa HTTP
El servicio A llama al servicio B a través de HTTP/REST.
OBTENER /usuarios/123.
Esto acopla la disponibilidad de A a B. Si B está inactivo, A falla. Si B es lento, A se bloquea. Si A llama a B, que llama a C, que llama a D, tienes una Cadena de llamadas. Si cada servicio tiene una disponibilidad del 99,9%, una cadena de 4 servicios tiene una disponibilidad de €0,999^4 \aproximadamente 99,6%€. Estás diseñando un fracaso válido.
gRPC: la variante de alto rendimiento
Para la comunicación interna, preferimos gRPC (Protobufs) a REST/JSON.
- Binary Packed: cargas útiles 10 veces más pequeñas.
- Fuertemente escrito: la generación de código garantiza que el Servicio A sepa exactamente lo que espera el Servicio B.
- Multiplexación: compatibilidad con HTTP/2 lista para usar.
Asíncrono: la arquitectura basada en eventos
Éste es el santo grial del desacoplamiento. El servicio A no llama al servicio B. El servicio A emite un evento.
- Registros de usuarios (Servicio A).
- El servicio A publica el evento “USER_REGISTERED” en Kafka/RabbitMQ.
- El servicio B (correo electrónico) consume el evento -> envía correo electrónico de bienvenida.
- El servicio C (Análisis) consume evento -> Panel de actualizaciones.
Si el Servicio B está fuera de línea (Mantenimiento, Fallo), el Servicio A continúa funcionando. El mensaje se encuentra en la cola. Cuando el Servicio B vuelve a estar en línea, procesa el trabajo pendiente. Esto es Resiliencia.
Coherencia de los datos: la parte más difícil
En un Monolito, tienes Transacciones ACID.
COMENZAR LA TRANSACCIÓN; INSERTAR Usuario; INSERTAR Cuenta; COMPROMETER;
O suceden ambas cosas o no sucede ninguna.
En Microservicios, tienes Base de datos por servicio.
El servicio A tiene users_db. El servicio B tiene accounts_db. No puede ejecutar una transacción en dos servidores de bases de datos diferentes.
El patrón de la saga
¿Cómo se maneja una transacción distribuida? Escenario: el usuario realiza un pedido.
- Servicio de Inventario: Stock de reserva.
- Servicio de Pago: Tarjeta de cargo.
- Servicio de envío: Imprimir etiqueta.
¿Qué pasa si falla la “Tarjeta de cargo”? Ya tienes stock reservado. Debes “Deshacer” el paso 1. Esta es una Transacción compensatoria.
diagrama de secuencia
participante O como orquestador de órdenes
participante I como Inventario
participante P como pago
O->>I: Stock de reserva (Artículo X)
I-->>O: Éxito
O->>P: Cargo $100
P-->>O: Fallo (nsf)
O->>I: LIBERAR Stock (Compensar)
I-->>O: Éxito
O-->>Usuario: Orden fallida
Implementar Sagas correctamente es increíblemente complejo. Necesita un orquestador de máquina de estado (como AWS Step Functions estándar o Temporal.io). Si no necesita esta complejidad, no cree microservicios.
Observabilidad: ver en la oscuridad
En un monolito, la depuración es tail -f /var/log/app.log.
En los microservicios, una sola solicitud de usuario puede llegar a 10 contenedores diferentes en 10 nodos diferentes.
Debe implementar el rastreo distribuido (OpenTelemetry).
Cada solicitud obtiene un “TraceID” en Ingress Load Balancer. Este ID se propaga en encabezados HTTP (X-B3-TraceId) a todos los servicios posteriores.
Herramientas como Jaeger, Datadog o Honeycomb visualizan la “cascada” de la solicitud.
- “¿Por qué estos 500 ms fueron lentos?”
- “Oh, el Servicio D tardó 450 ms en una consulta de base de datos”.
Sin rastrear, estás volando a ciegas.
Infraestructura: ejemplo de código
Usamos Terraform para aprovisionar la infraestructura para servicios independientes.
# pago-servicio.tf
recurso "kubernetes_deployment" "pago" {
metadatos {
nombre = "servicio-pago"
}
especificación {
réplicas = 3
seleccionador {
etiquetas_partidas = {
aplicación = "pago"
}
}
plantilla {
metadatos {
etiquetas = {
aplicación = "pago"
}
}
especificación {
contenedor {
imagen = "código de casa/pago:v1.2"
nombre = "pago"
# La regla de oro: limitar los recursos
recursos {
límites = {
procesador = "500 m"
memoria = "512Mi"
}
solicitudes = {
procesador = "250 m"
memoria = "256Mi"
}
}
entorno {
nombre = "DB_HOST"
valor_de {
referencia_clave_secreta {
nombre = "pago-db-creds"
clave = "anfitrión"
}
}
}
}
}
}
}
}
La estrategia de descomposición: el higo estrangulador
Si tienes un monolito heredado, no lo reescribas desde cero. Utilice el Patrón de higo estrangulador.
- Coloque un Proxy (API Gateway) frente al Monolith.
- Dirija todo el tráfico hacia Monolith.
- Identify one bounded context (e.g., “Reviews”).
- Cree un “Servicio de reseñas”.
- Cambiar proxy: si la ruta es
/api/reviews, diríjase al nuevo servicio. De lo contrario, ruta hacia Monolith. - Repita hasta que desaparezca el Monolito.
11. El papel de API Gateway
No desea que 10 servicios manejen la autenticación de forma independiente. Coloca una puerta de enlace (Kong, Tyk o AWS API Gateway) en el borde. Se encarga de:
- Autentificación: Validación JWT.
- Límite de velocidad: 100 solicitudes/min por IP.
- Almacenamiento en caché: almacena en caché las solicitudes GET públicas.
- Enrutamiento:
/api/v1/users-> Servicio de usuario. Esto mantiene sus servicios internos “tontos” y centrados en la lógica empresarial.
12. Malla de servicios (Istio / Linkerd)
Cuando tiene 50 servicios, el “Servicio A” que se comunica con el “Servicio B” necesita cifrado (mTLS). Administrar certificados manualmente es un infierno. Una Service Mesh inyecta un “Sidecar Proxy” (Envoy) al lado de cada contenedor. El proxy maneja:
- mTLS: Cifrado automático.
- Reintentos: “Si falla, vuelva a intentarlo 3 veces con un retroceso exponencial”.
- Rotura de circuito: “Si el servicio B tiene un 50 % de errores, deja de llamarlo durante 1 minuto”. Desacopla la lógica de red del código de aplicación.
13. Pruebas de contrato (Pacto)
¿Cómo se prueba el Servicio A sin activar el Servicio B?
Pruebas por contrato.
El Servicio A (Consumidor) escribe un “Archivo Pacto”:
“Espero que GET /user devuelva { id: string }”.
El servicio B (proveedor) ejecuta una prueba con este archivo Pact en su canal de CI.
Si el Servicio B cambia “id” a “userId”, la compilación falla.
Esto detecta cambios importantes antes de la implementación.
14. Conclusión
Los microservicios son un patrón de escalamiento organizacional, no una optimización del rendimiento. Introducen Latencia de red, Problemas de coherencia y Complejidad operativa a cambio de Velocidad del equipo y Escalabilidad independiente.
Si sois un equipo de 5 desarrolladores, estáis construyendo un Monolito. Podrías llamarlo Microservicios, pero solo estás construyendo una máquina de dolor distribuida.
Escale su estructura de código antes de escalar su infraestructura.
**[Contrate a nuestros arquitectos](/contact)**.