main content

Cómo Diseñar una Arquitectura de Microservicios para Tu Aplicación Web a Medida

Si has estado en una kickoff técnica en los últimos cinco años, conoces la pregunta: "¿microservicios o monolito?" Y conoces la respuesta honesta: depende. Lo que no suele decirse en la reunión es qué depende de qué. Depende del problema concreto que quieres resolver, del tamaño y madurez del equipo, y de cuánta complejidad operativa puedes sostener hoy, no en la diapositiva que enseñas al inversor para 2029.

Lo que viene a continuación no es un manifiesto. Son las decisiones que de verdad importan cuando te toca diseñar una arquitectura de microservicios para una aplicación web a medida, contadas como se las contaría a alguien que está a punto de tomarlas por primera vez.

El monolito no es el enemigo

Llevamos años hablando mal del monolito y, sinceramente, casi siempre es injusto. Un monolito modular bien estructurado, con módulos internos claros y fronteras respetadas, aguanta perfectamente durante años. Shopify, sin ir más lejos, lleva escalando un monolito modular a tamaños que harían sudar a cualquier arquitectura distribuida.

Los microservicios, en cambio, no son un upgrade automático. Lo que ganas en autonomía de equipos lo pagas en complejidad distribuida: latencia de red donde antes había una llamada en memoria, consistencia eventual, despliegues que hay que coordinar, y una superficie de fallos que crece con el número de servicios. Si no tienes un equipo capaz de operar todo eso, los microservicios no te van a hacer la vida más fácil; te la van a complicar de formas creativas.

Cuándo empieza a tener sentido partir

  • Equipos que estorban entre sí: si tienes varios equipos trabajando sobre dominios distintos (pagos, catálogo, notificaciones) y se pisan en cada release, los microservicios les devuelven autonomía para desplegar a su ritmo.
  • Escalado selectivo: cuando un componente concreto recibe diez veces más carga que el resto, poder escalarlo de forma aislada es ahorro directo en factura cloud.
  • Stack políglota con razón: si un módulo necesita Python para machine learning y otro pide Go porque está cuello de botella en I/O, separarlos por servicio es lo natural.
  • Ritmos de release distintos: un servicio de facturación que cambia cada trimestre no debería estar atado al frontend que tocas cada semana.

Cuándo el monolito sigue ganando

  • Equipos pequeños, digamos menos de diez personas en el mismo producto.
  • Producto en fase temprana, donde el dominio aún se está descubriendo y las fronteras que dibujes hoy no van a sobrevivir al próximo trimestre.
  • Presupuesto ajustado para infraestructura y operaciones, que es casi todo el mundo en realidad.

Mi regla de cabecera: empieza con un monolito modular, identifica dónde duele de verdad y extrae el primer microservicio sólo cuando el coste de mantener el monolito supere al de operar servicios distribuidos. Un monolito modular suele ser mejor punto de partida que ocho microservicios mal cortados que tendrás que fusionar dentro de seis meses.

La puerta de entrada: el API gateway

En cuanto decides ir a microservicios, el primer ladrillo que vas a necesitar es un API gateway. Es la única puerta de entrada que ven los clientes y oculta lo que pasa detrás. Si mañana parte un servicio en dos, el cliente no se entera.

Lo que un gateway decente hace por ti:

  • Enrutamiento: dirige cada petición al servicio adecuado según ruta, cabeceras o contenido.
  • Autenticación y autorización: centraliza la validación de tokens JWT, claves API u OAuth. Te ahorra reimplementar lo mismo en cada servicio y, sobre todo, te ahorra que cada equipo lo implemente mal a su manera.
  • Rate limiting y throttling: protege los servicios backend de picos legítimos y de quien intenta tirarlos a propósito.
  • Transformación de peticiones: encaja lo que envía el cliente con lo que el servicio interno espera.
  • Agregación: junta respuestas de varios servicios en una sola llamada del cliente y recortas latencia percibida.

Las opciones que te vas a encontrar

Kong es la opción sólida si quieres open-source con un ecosistema enorme de plugins. Corre sobre Nginx y tiene versión comunitaria y enterprise con consola.

AWS API Gateway encaja si ya vives en AWS. Se integra de fábrica con Lambda, Cognito y CloudWatch. Pagas comodidad con vendor lock-in, ese es el trato.

Traefik y Envoy son los que han crecido fuerte en Kubernetes por su descubrimiento automático de servicios y configuración declarativa. Si tu plataforma es K8s, mira estos primero.

Contenedores y orquestación

Docker es el vehículo natural para empaquetar microservicios. Estandariza el entorno de ejecución y mata el "en mi máquina funciona" para siempre, o al menos hasta el siguiente bug raro de zona horaria.

Por qué casi todos acaban en Kubernetes

Cuando pasas de cuatro contenedores a cuarenta, necesitas que algo te los gestione. Kubernetes (K8s) es el estándar de facto, te guste más o menos. Lo que resuelve:

  • Scheduling: decide en qué nodo del clúster cae cada contenedor según recursos disponibles.
  • Auto-scaling: sube y baja réplicas en función de métricas (CPU, memoria, peticiones por segundo).
  • Self-healing: reinicia contenedores caídos, reemplaza nodos perdidos y saca del balanceo a instancias que dejan de responder a los health checks.
  • Rollouts y rollbacks: despliega en canary o blue-green y revierte solo si las métricas de error se disparan.

Aviso: K8s no es gratis aunque sea open-source. La curva de aprendizaje es real y el coste operativo también.

Alternativas si K8s te parece exagerado

Amazon ECS es la propuesta nativa de AWS. Menos flexible que K8s, mucho más fácil de operar si no necesitas portabilidad entre nubes. Con Fargate ni siquiera gestionas los servidores, te olvidas del plano de cómputo.

Si quieres Kubernetes pero no quieres operar el plano de control, EKS (AWS), GKE (Google Cloud) y AKS (Azure) te quitan ese dolor por una factura mensual razonable comparada con lo que cuesta un SRE.

Cómo hablan los servicios entre sí

Esta es de las decisiones con más impacto a largo plazo, y la mayoría de arquitecturas reales acaban combinando los dos grandes patrones.

Síncrono: REST y gRPC

En síncrono, el emisor envía una petición y se queda esperando respuesta antes de continuar.

REST sobre HTTP es lo universal. Fácil de entender, fácil de depurar con curl, fácil de documentar con OpenAPI/Swagger. Funciona bien para lecturas y para flujos donde la latencia de una llamada HTTP es asumible.

gRPC usa Protocol Buffers para serializar en binario y HTTP/2 para transportar. Es bastante más rápido que REST cuando tienes mucho tráfico interno entre servicios. La contrapartida es la visibilidad: depurar gRPC en producción a las tres de la mañana es menos agradable que depurar REST.

Asíncrono: colas y eventos

En asíncrono, el emisor publica un mensaje y sigue a lo suyo sin esperar respuesta.

Colas de mensajes (RabbitMQ, Amazon SQS) son punto a punto: un productor publica, un consumidor procesa. Perfecto para todo lo que puede diferirse: enviar emails, generar informes, procesar imágenes.

Event streaming (Apache Kafka, Amazon Kinesis) va por pub/sub. Los eventos quedan persistidos y varios consumidores pueden leerlos por su cuenta. Es la base de las arquitecturas event-driven y abre la puerta a event sourcing y CQRS, que son potentes pero no gratis en complejidad.

La regla práctica que aplico: síncrono cuando el cliente necesita respuesta ya; asíncrono para todo lo demás, sobre todo cuando cruzas fronteras de dominio y puedes vivir con consistencia eventual.

Service mesh: cuando la red se vuelve el problema

A partir de cierto número de servicios, gestionar la comunicación servicio-a-servicio se convierte en un problema propio. Un service mesh delega esa responsabilidad a una capa de infraestructura, normalmente con proxies sidecar que se inyectan junto a cada servicio. Tu código deja de saber que existe.

Lo que te resuelve:

  • mTLS automático: cifra todo el tráfico interno sin que nadie en el equipo tenga que tocar una línea.
  • Observabilidad sin instrumentar: métricas, trazas y logs de todo lo que cruza la red, sin meter manualmente librerías en cada servicio.
  • Circuit breaking y retries: patrones de resiliencia movidos a la capa de red, no enterrados en la aplicación.
  • Control de tráfico: canary deployments, traffic mirroring y fault injection para probar resiliencia sin romper a usuarios reales.

Istio es el más conocido y también el más complejo; te da todo y te lo cobra en horas de operación. Linkerd pesa menos, se opera más fácil y para equipos que arrancan suele ser el más sensato. Empieza por ahí salvo que tengas una razón muy clara para Istio.

Observabilidad: los tres pilares de siempre

Operar microservicios sin observabilidad es conducir de noche con los faros apagados. Tres pilares clásicos: métricas, logs y trazas distribuidas.

Métricas

Prometheus recolecta lo numérico (latencia, tasa de error, uso de recursos) y Grafana lo pinta. Las métricas contestan a "qué está pasando ahora mismo".

Logs

Logs centralizados con ELK Stack o Loki te dejan buscar y correlacionar eventos. La clave está en estructurarlos como JSON con campos estándar: timestamp, service_name, trace_id, level, message. Si los logs son texto libre, la herramienta te servirá de poco.

Trazas distribuidas

Una petición de usuario puede pasar por diez servicios antes de devolver nada. Las trazas distribuidas (con Jaeger o Zipkin, convergiendo ya en OpenTelemetry) te dejan seguir esa petición de punta a punta y ver dónde se cae o dónde se atasca. Sin esto, cada incidencia es una búsqueda a ciegas.

Mi consejo poco negociable: monta los tres pilares antes del primer despliegue a producción. Después siempre se hace tarde.

Dibujar las fronteras: Domain-Driven Design

El error más caro al diseñar microservicios casi nunca es tecnológico, es organizativo. La gente parte los servicios por capas técnicas (uno para la base de datos, otro para la lógica, otro para la API) o por la forma del equipo actual. Y dentro de un año, cuando el equipo cambia, las fronteras dejan de tener sentido.

Domain-Driven Design (DDD) propone otra cosa: identificar bounded contexts, áreas del negocio con lenguaje y reglas propias, y usarlos como guía para cortar los servicios. Un servicio de "pedidos" no tiene por qué saber cómo "inventario" guarda su stock por dentro; sólo necesita preguntarle si hay disponibilidad.

Cuando un equipo necesita ayuda para tomar este tipo de decisiones, dibujar las fronteras correctas y montar la infraestructura sin pegarse tres tortazos por el camino, contar con gente que ya ha hecho este recorrido marca la diferencia entre un sistema que escala y un sistema que se convierte en un lastre operativo. Si buscas ese acompañamiento, puedes hablar con Tangram Consulting sobre tu proyecto.

Datos en un mundo distribuido

Cada servicio debería ser dueño de sus datos. Compartir base de datos entre servicios es la vía rápida para acoplarlos por la puerta de atrás y bloquearos mutuamente en cada despliegue de schema.

Eso te obliga a aceptar consistencia eventual. Cuando un usuario hace un pedido, el servicio de pedidos registra la operación y emite un evento; el de inventario lo consume y actualiza stock de forma asíncrona. Durante unos milisegundos los datos están desincronizados. En la mayoría de escenarios de negocio eso es perfectamente asumible, aunque cueste explicárselo a alguien que vive en transacciones ACID.

Cuando la consistencia fuerte es innegociable (transacciones financieras, por ejemplo), existen patrones como Saga (orquestado o coreografiado) que coordinan transacciones distribuidas sin recurrir a un commit distribuido clásico (2PC), que escala francamente mal en este mundo.

Empieza pequeño, decide con datos

La tentación de diseñar la arquitectura perfecta desde el día cero es comprensible y casi siempre contraproducente. Las arquitecturas de microservicios que funcionan se construyen poco a poco: monolito modular primero, primer servicio extraído cuando los datos de producción lo piden, observabilidad desde el primer commit en producción para poder decidir con números y no con intuiciones.

Lo que no se mide no se optimiza. Y lo que se sobre-diseña antes de tener usuarios suele acabar reescrito. La arquitectura está al servicio del producto, no al revés.