La decisión más difícil al diseñar una arquitectura de microservicios no es la tecnología, sino dónde trazar las fronteras: qué entra en cada servicio y qué queda fuera. Una mala descomposición provoca servicios acoplados que hay que desplegar juntos, esquemas de datos compartidos y cambios que se propagan en cascada. Una buena descomposición produce servicios cohesivos, autónomos y fáciles de evolucionar.
En esta lección estudiaremos las dos estrategias principales de descomposición (por capacidad de negocio y por subdominio), introduciremos el concepto de bounded context del Diseño Guiado por el Dominio (DDD) y aprenderemos a calibrar el tamaño y la granularidad adecuados de un servicio.
Contenido
- El problema de las fronteras
- Estrategia 1: descomposición por capacidad de negocio
- Estrategia 2: descomposición por subdominio (DDD)
- Bounded Contexts y lenguaje ubicuo
- Granularidad y tamaño adecuado
- Acoplamiento, cohesión y el mapa de contextos
- Errores comunes y consejos
- Ejercicios
- Conclusión
- El problema de las fronteras
Una frontera mal puesta tiene un coste enorme: corregirla implica mover datos, reescribir APIs y coordinar varios equipos. El objetivo de una buena frontera es maximizar la cohesión dentro del servicio (las cosas que cambian juntas están juntas) y minimizar el acoplamiento entre servicios (cambiar uno no obliga a cambiar otros).
| Propiedad | Qué buscamos |
|---|---|
| Cohesión | Alta: lo que cambia junto, vive junto. |
| Acoplamiento | Bajo: los servicios dependen poco unos de otros. |
| Autonomía | Alta: cada servicio se despliega y opera solo. |
La regla de oro: descompón por dominio de negocio, no por capa técnica. Servicios como "servicio de base de datos", "servicio de validaciones" o "servicio de utilidades" son un antipatrón, porque ningún cambio de negocio se resuelve tocando un solo servicio.
- Estrategia 1: descomposición por capacidad de negocio
Una capacidad de negocio es algo que la organización hace para generar valor, independientemente de cómo lo implemente. Se identifican analizando qué hace la empresa, no qué tablas tiene.
En una aseguradora, capacidades típicas serían:
- Gestión de clientes
- Contratación de pólizas
- Tramitación de siniestros
- Facturación y cobros
- Reaseguro
# Cada capacidad de negocio se convierte en un candidato a servicio
capacidades:
contratacion_polizas:
acciones: [crear_poliza, renovar_poliza, anular_poliza]
eventos_publicados: [PolizaCreada, PolizaRenovada, PolizaAnulada]
tramitacion_siniestros:
acciones: [abrir_siniestro, peritar, indemnizar]
eventos_publicados: [SiniestroAbierto, SiniestroIndemnizado]Cada capacidad agrupa las acciones y los eventos que le pertenecen. Esta estrategia es estable, porque las capacidades de negocio cambian mucho menos a menudo que la tecnología.
- Estrategia 2: descomposición por subdominio (DDD)
El Diseño Guiado por el Dominio descompone el dominio total en subdominios, que clasifica en tres tipos:
| Tipo de subdominio | Definición | Estrategia recomendada |
|---|---|---|
| Núcleo (core) | Lo que diferencia a la empresa; su ventaja competitiva. | Invertir el mejor esfuerzo; desarrollo propio cuidado. |
| De soporte (supporting) | Necesario pero no diferenciador. | Desarrollo más sencillo, posible externalización parcial. |
| Genérico (generic) | Problema común ya resuelto en el mercado. | Comprar o usar solución estándar. |
Para una aseguradora, el cálculo actuarial de primas es probablemente un subdominio núcleo; la gestión documental es de soporte; y el envío de correos electrónicos es genérico (se usa un proveedor externo).
Esta clasificación guía las inversiones: no tiene sentido construir desde cero un subdominio genérico cuando existen soluciones maduras.
- Bounded Contexts y lenguaje ubicuo
Un bounded context (contexto delimitado) es una frontera explícita dentro de la cual un modelo del dominio y su vocabulario tienen un significado preciso y único. La idea central es que la misma palabra puede significar cosas distintas en contextos distintos.
Considera el término "Cliente":
- En Contratación, un cliente es un tomador con capacidad de contratar y un perfil de riesgo.
- En Facturación, un cliente es un deudor con una cuenta y unos recibos.
- En Siniestros, un cliente es un perjudicado o beneficiario.
Forzar un único modelo de "Cliente" para los tres contextos produce una clase gigantesca con decenas de campos que nadie entiende del todo. La solución de DDD es que cada bounded context tenga su propio modelo de Cliente, adaptado a sus necesidades.
// Mismo concepto, dos modelos según el bounded context.
// Contexto Contratación: nos importa el riesgo
package com.fiatc.contratacion;
public class Cliente {
private String id;
private PerfilRiesgo perfilRiesgo;
private List<Poliza> polizasVigentes;
}
// Contexto Facturación: nos importan los cobros
package com.fiatc.facturacion;
public class Cliente {
private String id; // mismo identificador para correlacionar
private FormaPago formaPago;
private List<Recibo> recibosPendientes;
}El id actúa como nexo entre contextos, pero cada modelo solo contiene lo que su dominio necesita. El lenguaje ubicuo es el vocabulario común que comparten desarrolladores y expertos de negocio dentro de un contexto, y que se refleja directamente en el código.
graph LR
subgraph "Contexto Contratación"
C1[Cliente: tomador]
end
subgraph "Contexto Facturación"
C2[Cliente: deudor]
end
subgraph "Contexto Siniestros"
C3[Cliente: beneficiario]
end
C1 -. mismo id .- C2
C2 -. mismo id .- C3Cada bounded context es un candidato natural a microservicio. Esta es la regla más fiable para trazar fronteras.
- Granularidad y tamaño adecuado
¿Cómo de grande debe ser un servicio? No hay una métrica mágica de líneas de código. Mejores indicadores:
- Cohesión funcional: un servicio debe poder describirse con una frase de negocio.
- Transaccionalidad: lo que necesita estar en una misma transacción ACID debería vivir en el mismo servicio.
- Ritmo de cambio: lo que cambia junto debe estar junto.
- Autonomía del equipo: un servicio cabe en la cabeza de un equipo pequeño.
| Síntoma | Diagnóstico | Acción |
|---|---|---|
| Muchas llamadas entre dos servicios para cada operación | Demasiado fragmentado | Fusionar |
| Para un cambio de negocio hay que tocar 5 servicios | Fronteras mal puestas | Rediseñar fronteras |
| Un servicio con docenas de responsabilidades dispares | Demasiado grande | Dividir |
| Necesitas transacciones distribuidas constantemente | División incorrecta | Reagrupar por consistencia |
El consejo práctico: empieza con servicios más grandes de lo que crees necesario y divídelos cuando aparezca dolor real. Es mucho más fácil dividir un servicio que fusionar dos.
- Acoplamiento, cohesión y el mapa de contextos
DDD propone documentar cómo se relacionan los contextos mediante un mapa de contextos. Algunos patrones de relación:
- Cliente/Proveedor: un contexto consume la API de otro; el proveedor debe respetar las necesidades del cliente.
- Conformista: el consumidor se adapta sin más al modelo del proveedor.
- Capa anticorrupción (ACL): el consumidor traduce el modelo ajeno al suyo propio para no contaminarse.
// Capa anticorrupción: traduce el modelo externo al modelo interno
public class ClienteAcl {
public Cliente traducir(ClienteExternoDTO dto) {
// No dejamos que el modelo externo "se cuele" en nuestro dominio
Cliente cliente = new Cliente();
cliente.setId(dto.getCodigo()); // mapeo de nombres
cliente.setPerfilRiesgo(calcular(dto)); // adaptación de semántica
return cliente;
}
}La capa anticorrupción es valiosísima al integrar sistemas heredados: aísla tu modelo limpio de las rarezas del sistema externo.
Errores Comunes y Consejos
- Descomponer por capas técnicas (datos, validación, lógica): cualquier cambio de negocio toca todos los servicios. Descompón por dominio.
- Un único modelo canónico para toda la empresa: produce modelos gigantes e inmanejables. Adopta bounded contexts.
- Servicios demasiado pequeños desde el principio: empieza grande y divide ante el dolor real.
- Ignorar a los expertos de negocio: las fronteras correctas surgen de conversaciones con quienes conocen el dominio (técnicas como el Event Storming ayudan).
- Compartir tablas entre contextos: rompe la frontera. Usa APIs o eventos.
Ejercicios
- Explica con tus palabras qué es un bounded context y por qué la misma palabra ("Cuenta", "Producto"...) puede necesitar modelos distintos en contextos distintos.
- Clasifica los siguientes subdominios de un banco en núcleo, de soporte o genérico, y justifica: (a) motor de scoring crediticio, (b) gestión de plantillas de documentos, (c) envío de SMS.
- Te encuentras dos servicios, "Pedidos" y "Líneas de pedido", que se llaman mutuamente decenas de veces por operación y comparten transacciones. ¿Qué diagnóstico haces y qué acción tomas?
Soluciones
-
Un bounded context es una frontera dentro de la cual un modelo y su vocabulario tienen un significado único y coherente. La misma palabra necesita modelos distintos porque cada contexto se interesa por atributos y comportamientos diferentes: "Producto" en catálogo lleva descripción e imágenes, mientras que en almacén lleva ubicación y stock. Unificarlos crea un modelo sobrecargado.
-
(a) Núcleo: el scoring diferencia al banco y es su ventaja competitiva; desarrollo propio cuidado. (b) Soporte: necesario pero no diferenciador; desarrollo sencillo. (c) Genérico: problema resuelto; se contrata un proveedor de SMS.
-
Diagnóstico: están demasiado fragmentados; la fragmentación cruza una frontera transaccional. Acción: fusionarlos en un único servicio "Pedidos" que contenga las líneas como parte de su agregado, eliminando las llamadas de red y permitiendo transacciones ACID locales.
Conclusión
Hemos visto que la clave de una buena arquitectura de microservicios está en trazar fronteras por dominio de negocio, apoyándonos en las capacidades de negocio y, sobre todo, en los bounded contexts del DDD. La granularidad correcta busca alta cohesión y bajo acoplamiento, y conviene empezar con servicios grandes y dividir ante el dolor real.
Una vez definidos los servicios y sus fronteras, surge la pregunta práctica de cómo se hablan entre sí sin acoplarse en exceso. La siguiente lección, API Gateway, Service Discovery y Comunicación entre Servicios, aborda precisamente los mecanismos de comunicación síncrona y asíncrona, y la infraestructura que los hace posibles.
Curso de Arquitectura de Aplicaciones
Módulo 1: Fundamentos de la Arquitectura de Aplicaciones
- ¿Qué es la Arquitectura de Aplicaciones?
- El Rol del Arquitecto de Software
- Atributos de Calidad y Requisitos No Funcionales
- Decisiones Arquitectónicas y Compromisos (Trade-offs)
- Documentación de Arquitectura: Vistas y el Modelo C4
Módulo 2: Principios y Tácticas de Diseño
- Acoplamiento, Cohesión y Separación de Responsabilidades
- Principios SOLID Aplicados a la Arquitectura
- DRY, KISS, YAGNI y Otros Principios de Diseño
- Tácticas Arquitectónicas para los Atributos de Calidad
- Gestión de la Deuda Técnica
Módulo 3: Estilos y Patrones Arquitectónicos
- Arquitectura Monolítica
- Arquitectura en Capas (N-Tier)
- Arquitectura Cliente-Servidor
- Arquitectura Hexagonal (Puertos y Adaptadores)
- Arquitectura Limpia y Cebolla (Clean & Onion)
Módulo 4: Arquitecturas Distribuidas y Microservicios
- Introducción a los Sistemas Distribuidos
- Arquitectura de Microservicios
- Descomposición de Servicios y Bounded Contexts
- API Gateway, Service Discovery y Comunicación entre Servicios
- Patrones de Resiliencia: Circuit Breaker, Retry y Bulkhead
- El Teorema CAP y la Consistencia de Datos
Módulo 5: Arquitecturas Dirigidas por Eventos y Mensajería
- Fundamentos de la Arquitectura Orientada a Eventos
- Mensajería Asíncrona: Colas y Brokers
- Patrones de Eventos: Event Sourcing y CQRS
- Gestión de Transacciones Distribuidas: Patrón Saga
- Streaming de Datos en Tiempo Real
Módulo 6: Diseño Dirigido por el Dominio (DDD)
- Conceptos Fundamentales del DDD
- Diseño Estratégico: Bounded Contexts y Lenguaje Ubicuo
- Diseño Táctico: Entidades, Agregados y Repositorios
- Mapeo de Contextos (Context Mapping)
Módulo 7: Datos y Persistencia
- Estrategias de Persistencia: SQL vs NoSQL
- Patrones de Acceso a Datos: Repository, Unit of Work y DAO
- Base de Datos por Servicio y Gestión de Datos Distribuidos
- Caché y Estrategias de Invalidación
Módulo 8: Arquitectura en la Nube y Despliegue
- Fundamentos del Cloud Computing (IaaS, PaaS, SaaS)
- Contenedores y Orquestación con Docker y Kubernetes
- Arquitectura Serverless
- Patrones de Diseño Cloud-Native
- Infraestructura como Código (IaC)
Módulo 9: Calidad, Seguridad y Observabilidad
- Escalabilidad: Horizontal vs Vertical y Balanceo de Carga
- Alta Disponibilidad y Tolerancia a Fallos
- Seguridad por Diseño y Autenticación/Autorización
- Observabilidad: Logging, Métricas y Trazabilidad
- Rendimiento y Pruebas de Carga
