Los microservicios ofrecen enormes beneficios en escalabilidad y flexibilidad, pero también introducen complejidad. Conocer los patrones correctos y evitar anti-patrones es crucial para el éxito de tu arquitectura.
Patrones fundamentales de microservicios
1. API Gateway
El API Gateway actúa como punto de entrada único para todos los clientes:
- Enrutamiento de solicitudes a servicios apropiados
- Agregación de respuestas de múltiples servicios
- Autenticación y autorización centralizada
- Rate limiting y throttling
- Transformación de protocolos (REST a gRPC)
- Caché de respuestas frecuentes
Cuándo usarlo: Siempre en arquitecturas de microservicios. Es esencial para clientes web y móviles que no deberían conocer todos los servicios internos.
2. Service Discovery
Permite que los servicios se encuentren dinámicamente:
- Registro automático de instancias de servicio
- Health checks para detectar servicios no disponibles
- Balanceo de carga client-side
- Actualización dinámica sin reconfiguración
- Integración con Consul, Eureka, o Kubernetes DNS
Soluciones populares: Consul, Eureka, etcd, Kubernetes Service Discovery
3. Circuit Breaker
Previene fallos en cascada cuando un servicio no responde:
- Estados: Closed (normal), Open (bloqueando llamadas), Half-Open (probando)
- Timeout configurables para llamadas
- Umbral de fallos antes de abrir circuito
- Fallback automático a respuestas predeterminadas
- Métricas de estado del circuito
Librerías: Hystrix (Netflix), Resilience4j, Polly (.NET)
4. Event Sourcing
Almacena cambios de estado como secuencia de eventos:
- Historial completo de todos los cambios
- Posibilidad de reconstruir estado en cualquier punto
- Auditoría completa automática
- Event replay para debugging
- Proyecciones múltiples del mismo stream de eventos
Casos de uso: Sistemas financieros, e-commerce, cualquier dominio que requiera auditoría completa
5. CQRS (Command Query Responsibility Segregation)
Separa operaciones de lectura y escritura:
- Modelos optimizados por separado
- Escalado independiente de lecturas y escrituras
- Base de datos de lectura puede ser eventual consistency
- Queries complejos sin impactar escrituras
- Combina bien con Event Sourcing
6. Saga Pattern
Maneja transacciones distribuidas a través de múltiples servicios:
- Secuencia de transacciones locales
- Compensación automática en caso de fallo
- Dos estilos: Coreografía (eventos) u Orquestación (coordinador central)
- Mantiene consistencia eventual
Ejemplo: Procesar pedido → Reservar inventario → Procesar pago → Enviar pedido (con compensaciones si algo falla)
7. Strangler Fig Pattern
Migración gradual de monolito a microservicios:
- Interceptar requests al monolito
- Redireccionar funcionalidad migrada a nuevos servicios
- Mantener monolito funcionando durante migración
- "Estrangular" gradualmente el sistema legacy
8. Backend for Frontend (BFF)
APIs específicas para cada tipo de cliente:
- BFF móvil con datos optimizados para mobile
- BFF web con datos para navegadores
- BFF IoT con protocolos ligeros
- Reduce over-fetching y under-fetching
Anti-patrones a evitar
1. Distributed Monolith
El peor de los mundos: complejidad de microservicios sin los beneficios:
- Servicios altamente acoplados
- Deploys coordinados requeridos
- Compartir base de datos entre servicios
- Llamadas síncronas en cadena largas
- Lógica de negocio duplicada
Señales de alerta: No puedes desplegar un servicio sin actualizar otros, cambios requieren modificar múltiples servicios simultáneamente.
2. Nano-services
Servicios demasiado granulares:
- Overhead de red excesivo
- Operaciones simples requieren múltiples llamadas
- Debugging extremadamente complejo
- Costo de mantenimiento no justificado
Guía: Un microservicio debería poder ser mantenido por un equipo pequeño (2-pizza team). Si tu servicio solo tiene 2-3 endpoints, probablemente es demasiado pequeño.
3. Shared Database
Múltiples servicios accediendo a la misma base de datos:
- Acoplamiento a nivel de esquema de BD
- Imposible cambiar esquema sin coordinar
- Contención de recursos de BD
- Pérdida de autonomía de servicios
- Dificulta migración de tecnologías
Regla de oro: Cada microservicio debe tener su propia base de datos. Comunicación entre servicios solo vía APIs.
4. Chatty Services
Excesiva comunicación entre servicios:
- Latencia acumulada de múltiples network hops
- Riesgo de fallos en cascada
- Difícil de debuggear y monitorear
- Indica boundaries incorrectos
Solución: Re-evaluar domain boundaries. Considera agrupar servicios muy comunicativos o usar eventos asíncronos.
5. Mega Service
Servicio que hace demasiado:
- Múltiples bounded contexts mezclados
- Equipos grandes necesarios
- Deploys riesgosos
- Dificulta escalado selectivo
6. Hardcoded Endpoints
URLs de servicios codificadas en la aplicación:
- Imposibilita escalado dinámico
- Dificulta deploys blue-green
- No permite failover automático
- Problemas en entornos cloud
Usa siempre: Service discovery o configuration management (Consul, Spring Cloud Config)
7. Trying to Do Everything Right Away
Implementar todos los patrones desde el día 1:
- Over-engineering para necesidades actuales
- Complejidad innecesaria
- Tiempo de desarrollo excesivo
- Dificultad para onboarding de equipo
Enfoque mejor: Empezar simple, añadir patrones conforme los necesitas basado en problemas reales.
Patrones de resiliencia
Retry con backoff exponencial
Reintentos inteligentes ante fallos transitorios:
- Espera 1s, 2s, 4s, 8s entre reintentos
- Jitter aleatorio para evitar thundering herd
- Máximo de reintentos configurables
- Solo para errores recuperables (5xx, no 4xx)
Timeout agresivos
No esperar indefinidamente:
- Timeouts cortos para llamadas síncronas
- Fail fast es mejor que hang indefinido
- Permite circuit breaker detectar problemas rápido
Bulkhead
Aislar recursos para prevenir que un fallo afecte todo:
- Thread pools separados por servicio dependiente
- Connection pools aislados
- Si un servicio externo falla, no agota todos los recursos
Patrones de comunicación
Síncrono vs Asíncrono
Síncrono (REST, gRPC):
- Más simple de implementar y debuggear
- Respuesta inmediata
- Acoplamiento temporal fuerte
- Requiere ambos servicios disponibles
Asíncrono (Message Queue, Events):
- Desacoplamiento temporal
- Mayor resiliencia
- Mejor para operaciones de larga duración
- Complejidad en debugging y monitoreo
Request/Response vs Event-Driven
Usa eventos cuando:
- Múltiples servicios interesan en mismo evento
- No necesitas respuesta inmediata
- Quieres desacoplar productor de consumidores
Usa request/response cuando:
- Necesitas respuesta inmediata
- Operación transaccional
- Comunicación uno-a-uno
Patrones de datos
Database per Service
Cada servicio gestiona su propia base de datos:
- Autonomía completa del equipo
- Tecnología de BD apropiada para cada caso
- Escalado independiente
- Desafío: queries cross-service y transacciones
API Composition
Combinar datos de múltiples servicios:
- API Gateway agrega datos
- Join de datos en capa de aplicación
- Puede causar problemas de performance
Data Replication
Replicar datos necesarios en cada servicio:
- Reduce llamadas cross-service
- Mejor performance de lectura
- Eventual consistency
- Sincronizar vía eventos
Mejores prácticas generales
Diseño Domain-Driven
- Identificar Bounded Contexts correctamente
- Un microservicio = Un bounded context (generalmente)
- Ubiquitous language dentro de cada servicio
- Context maps para relaciones entre servicios
Observabilidad
- Logging estructurado con correlation IDs
- Distributed tracing (Jaeger, Zipkin)
- Métricas con Prometheus + Grafana
- Health checks y readiness probes
Testing
- Unit tests extensivos
- Integration tests con test containers
- Contract testing (Pact) entre servicios
- Chaos engineering en producción
Deployment
- CI/CD por servicio independiente
- Containerización (Docker)
- Orquestación (Kubernetes)
- Blue-green o canary deployments
Conclusión
Los microservicios no son una bala de plata. Requieren disciplina, patrones correctos y evitar anti-patrones comunes. Empezar con un monolito bien estructurado suele ser mejor que microservicios mal diseñados.
Los patrones como API Gateway, Circuit Breaker y Service Discovery son fundamentales. Los anti-patrones como Distributed Monolith o Shared Database pueden destruir tu arquitectura.
En Trixasoft, evaluamos cuidadosamente cada caso. No todos los proyectos necesitan microservicios. Cuando sí son apropiados, aplicamos estos patrones de manera pragmática, añadiendo complejidad solo cuando aporta valor real.