Ningún Bounded Context vive aislado. En un sistema real, los contextos necesitan colaborar: el de Suscripción consume datos del de Ventas, el de Facturación reacciona a las pólizas suscritas, etcétera. El Mapeo de Contextos (Context Mapping) es la disciplina de DDD que describe cómo se relacionan e integran los distintos Bounded Contexts y, sobre todo, qué tipo de relación de poder, dependencia y traducción existe entre ellos. Esta lección cierra el módulo conectando el diseño estratégico con la realidad organizativa: los patrones de mapeo no solo describen integraciones técnicas, sino también relaciones entre equipos. Comprenderlos te permitirá diseñar integraciones sanas y protegerte de modelos ajenos que podrían contaminar el tuyo.
Contenido
- ¿Qué es un Context Map y por qué importa?
- Patrones de relación entre contextos
- El Anti-Corruption Layer (ACL) en detalle
- El Open Host Service y el Published Language
- Dibujar un Context Map de ejemplo
- ¿Qué es un Context Map y por qué importa?
Un Context Map es una representación —normalmente un diagrama— de todos los Bounded Contexts de un sistema y las relaciones entre ellos. No describe el detalle interno de cada contexto (eso es el diseño táctico), sino las fronteras y los puntos de contacto.
Su valor es doble:
- Técnico: muestra qué contexto depende de qué, por dónde fluyen los datos y dónde hacen falta traducciones.
- Organizativo: las relaciones entre contextos suelen reflejar relaciones entre equipos. Un contexto "río arriba" (upstream) impone su modelo; uno "río abajo" (downstream) debe adaptarse. Saber quién está arriba y quién abajo evita conflictos.
Dos conceptos transversales que aparecen en todos los patrones:
- Upstream (río arriba): el contexto que provee, del que otros dependen. Tiene más "poder": sus cambios afectan a los demás.
- Downstream (río abajo): el contexto que consume, que depende del de arriba y debe adaptarse a sus cambios.
- Patrones de relación entre contextos
DDD cataloga varios patrones de relación. Estos son los más importantes:
| Patrón | Relación de poder | Descripción | Cuándo usarlo |
|---|---|---|---|
| Partnership (asociación) | Igualitaria | Dos contextos/equipos dependen mutuamente y coordinan sus cambios de forma conjunta. Éxito o fracaso compartidos. | Equipos que deben evolucionar a la vez y se comunican bien |
| Shared Kernel (núcleo compartido) | Igualitaria | Comparten una porción del modelo y código (un módulo común). Cambiarlo requiere acuerdo de ambos. | Subdominio común muy estable entre dos equipos cercanos |
| Customer-Supplier (cliente-proveedor) | Upstream/Downstream | El proveedor (upstream) atiende las necesidades del cliente (downstream); el cliente puede influir en el plan del proveedor. | El upstream está dispuesto a priorizar las necesidades del downstream |
| Conformist (conformista) | Upstream/Downstream | El downstream adopta el modelo del upstream tal cual, sin traducirlo, porque no tiene capacidad de influir. | El upstream no negocia (ej. un sistema externo grande) y su modelo es aceptable |
| Anti-Corruption Layer (ACL) | Upstream/Downstream | El downstream construye una capa de traducción que aísla y convierte el modelo ajeno al suyo propio. | El modelo del upstream es malo o ajeno y no quieres que contamine el tuyo |
| Open Host Service (OHS) | Upstream | El upstream publica una API/protocolo bien definido para que muchos consumidores se integren de forma estándar. | Muchos clientes consumen el mismo contexto |
La diferencia clave entre Conformist y ACL es crucial:
- Conformist: "Me trago tu modelo tal cual, con sus defectos, porque me sale más barato que traducir."
- ACL: "No quiero tu modelo dentro de mi casa; pongo una capa que lo traduce al mío y me protege de tus cambios."
- El Anti-Corruption Layer (ACL) en detalle
El Anti-Corruption Layer es probablemente el patrón más útil en sistemas que se integran con software legado o externo. Su misión es impedir que el modelo de otro contexto se filtre y corrompa el nuestro.
Imagina que tu contexto de Suscripción debe consultar un viejo sistema de scoring crediticio externo que devuelve datos crudos y con un modelo muy distinto al tuyo:
// Modelo CRUDO que devuelve el sistema externo (no queremos esto en nuestro dominio)
public class RespuestaScoringExterno {
public int cod_riesgo; // 1, 2, 3... ¿qué significan?
public String flag_moroso; // "S" / "N"
public double pct; // ¿de qué?
}Este modelo externo es feo: códigos numéricos sin significado, flags con cadenas, nombres crípticos. Si lo dejamos entrar tal cual, contamina todo nuestro dominio.
// ACL: traduce el modelo externo a NUESTRO modelo limpio
public class ScoringCrediticioAcl {
private final ClienteSistemaExterno externo; // el sistema legado
public ScoringCrediticioAcl(ClienteSistemaExterno externo) {
this.externo = externo;
}
public PerfilCrediticio obtenerPerfil(IdAsegurado id) {
RespuestaScoringExterno raw = externo.consultar(id.valor());
// TRADUCCIÓN: del modelo ajeno al nuestro, limpio y con significado
NivelRiesgo nivel = traducirNivel(raw.cod_riesgo);
boolean moroso = "S".equals(raw.flag_moroso);
return new PerfilCrediticio(nivel, moroso); // objeto de NUESTRO dominio
}
private NivelRiesgo traducirNivel(int cod) {
switch (cod) {
case 1: return NivelRiesgo.BAJO;
case 2: return NivelRiesgo.MEDIO;
default: return NivelRiesgo.ALTO;
}
}
}Lo que conseguimos con el ACL:
- El sistema externo y su modelo feo quedan encapsulados detrás de
ScoringCrediticioAcl. El resto de nuestro dominio nunca veRespuestaScoringExterno. - La traducción convierte
cod_riesgo(unintsin significado) en unNivelRiesgo(un concepto claro de nuestro dominio) y el flag"S"/"N"en unboolean. - Nuestro dominio recibe un
PerfilCrediticiolimpio, expresado en nuestro Lenguaje Ubicuo. - Si el sistema externo cambia su formato, solo cambiamos el ACL; el resto del dominio queda intacto. Esta es la protección clave.
graph LR
subgraph Nuestro[Bounded Context: Suscripcion]
D[Dominio limpio]
ACL[Anti-Corruption Layer]
end
subgraph Externo[Sistema externo legado]
E[Modelo feo / crudo]
end
D --> ACL
ACL --> EEl ACL actúa como una membrana: hacia dentro habla nuestro idioma; hacia fuera, el del sistema externo.
- El Open Host Service y el Published Language
Cuando un contexto upstream tiene muchos consumidores, no es razonable adaptarse a cada uno. En su lugar, publica un Open Host Service (OHS): una API estable y documentada que cualquiera puede consumir de forma estándar. Junto al OHS suele ir un Published Language: un formato de intercambio bien definido y versionado (por ejemplo, un esquema JSON o un contrato OpenAPI).
# Published Language: contrato del evento publicado por el OHS de Polizas
PolizaSuscritaV1:
type: object
required: [idPoliza, idAsegurado, fechaEfecto, primaAnual]
properties:
idPoliza: { type: string, format: uuid }
idAsegurado: { type: string, format: uuid }
fechaEfecto: { type: string, format: date }
primaAnual:
type: object
properties:
importe: { type: number }
moneda: { type: string, example: "EUR" }Por qué este contrato es un buen Published Language:
- Es explícito y versionado (
PolizaSuscritaV1): los consumidores saben exactamente qué esperar, y una futuraV2no rompe a quienes usan laV1. - Define los campos obligatorios (
required) y sus tipos: es un acuerdo formal, no una estructura que cambia sin avisar. - Está expresado en un formato neutral (esquema tipo JSON/OpenAPI), independiente de la tecnología interna del contexto que lo publica.
Con un OHS y un Published Language, el contexto de Pólizas puede tener decenas de consumidores (Facturación, Notificaciones, Reaseguro...) sin acoplarse a ninguno: todos hablan el lenguaje publicado.
- Dibujar un Context Map de ejemplo
Pongamos juntos los patrones en el Context Map de nuestra aseguradora:
graph TD
Ventas -->|Customer-Supplier| Suscripcion
Suscripcion -->|ACL| ScoringExterno[Scoring Crediticio EXTERNO]
Suscripcion -->|publica eventos OHS| Polizas
Polizas -->|Open Host Service| Facturacion
Polizas -->|Open Host Service| Notificaciones
Polizas <-->|Partnership| SiniestrosLectura del mapa y justificación de cada relación:
- Ventas → Suscripción (Customer-Supplier): Ventas es upstream y provee los leads; Suscripción es downstream y puede pedir a Ventas que incluya ciertos datos. Hay diálogo entre equipos.
- Suscripción → Scoring Externo (ACL): el sistema de scoring es externo y su modelo no nos gusta, así que lo aislamos tras una capa anticorrupción.
- Pólizas → Facturación / Notificaciones (Open Host Service): Pólizas tiene varios consumidores, así que publica una API/eventos estándar para todos.
- Pólizas ↔ Siniestros (Partnership): ambos evolucionan juntos y dependen mutuamente (un siniestro necesita la póliza vigente; la póliza refleja la siniestralidad), por lo que sus equipos coordinan los cambios de forma conjunta.
Este mapa permite a la organización ver de un vistazo dónde están las dependencias críticas, dónde hace falta protección (ACL) y dónde conviene estabilizar contratos (OHS).
Errores Comunes y Consejos
- No tener Context Map. Sin él, las integraciones crecen de forma caótica y nadie sabe quién depende de quién. Dibújalo y manténlo actualizado.
- Ser Conformist por pereza cuando el modelo ajeno es malo. Adoptar tal cual un modelo defectuoso contamina el tuyo. Si el modelo externo es feo o inestable, invierte en un ACL.
- Compartir base de datos entre contextos. Es el peor "patrón": acopla los contextos por la BD y rompe sus fronteras. Intégralos por API o eventos, nunca por tablas compartidas.
- Shared Kernel demasiado grande. Cuanto más compartes, más coordinación necesitas. Mantén el núcleo compartido mínimo y muy estable, o no lo uses.
- No versionar el Published Language. Si cambias el contrato sin versión, rompes a todos los consumidores a la vez. Versiona siempre (V1, V2...).
- Consejo: las relaciones del Context Map son tanto técnicas como organizativas. Antes de elegir un patrón, pregúntate qué equipo tiene el poder y la disposición a colaborar; eso suele decidir entre Customer-Supplier, Conformist o ACL.
Ejercicios
Ejercicio 1. Tu contexto debe integrarse con la API de un proveedor de pagos externo, grande y que no va a adaptar nada a ti, pero cuyo modelo es razonablemente limpio. ¿Qué patrón eliges y por qué? ¿Y si el modelo fuera caótico?
Ejercicio 2. Dos equipos comparten un módulo de "cálculo de impuestos" que ambos modifican constantemente y que provoca conflictos frecuentes. Identifica qué patrón están usando y propón una alternativa más sana.
Ejercicio 3. Para cada relación, indica cuál es el contexto upstream y cuál el downstream: (a) un contexto de Informes que lee datos del contexto de Ventas; (b) un Open Host Service de Catálogo consumido por Pedidos.
Soluciones
Solución 1. Si el modelo del proveedor es limpio y no negocia, el patrón natural es Conformist: adoptas su modelo tal cual porque es aceptable y traducirlo no compensaría. Si el modelo fuera caótico o inestable, lo correcto es un Anti-Corruption Layer que aísle tu dominio de ese modelo y te proteja de sus cambios.
Solución 2. Están usando un Shared Kernel (núcleo compartido) que es demasiado grande y volátil, de ahí los conflictos constantes. Alternativas más sanas: convertir el cálculo de impuestos en un contexto propio con un Open Host Service que ambos consuman, o asignar la propiedad a un equipo (upstream) y que el otro lo consuma como Customer-Supplier. Así se elimina la necesidad de modificar a la vez un código compartido.
Solución 3. (a) Ventas es upstream (provee los datos); Informes es downstream (depende de ellos). (b) Catálogo es upstream (publica el OHS); Pedidos es downstream (consume el servicio).
Conclusión
Con esta lección cerramos el módulo de Diseño Dirigido por el Dominio. Hemos visto que el Context Map revela cómo se relacionan los Bounded Contexts y que esas relaciones son a la vez técnicas y organizativas. Hemos estudiado los patrones clave —Partnership, Shared Kernel, Customer-Supplier, Conformist, ACL y Open Host Service— y la diferencia decisiva entre conformarse con un modelo ajeno (Conformist) y protegerse de él con una capa de traducción (ACL). Por último, hemos compuesto un Context Map completo que integra todos los patrones.
Con los cuatro pilares del módulo —conceptos fundamentales, diseño estratégico, diseño táctico y mapeo de contextos— dispones ya de un marco completo para abordar dominios complejos. El siguiente paso natural en el curso de Arquitectura de Aplicaciones es ver cómo estos contextos y modelos se materializan en estilos arquitectónicos concretos, como la arquitectura hexagonal y las arquitecturas dirigidas por eventos, que llevan a la práctica todo lo aprendido aquí.
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
