El plan de descomposición de la lección anterior deja seis servicios con fronteras dibujadas a trazo grueso. Falta lo que separa un diseño aceptable de uno bueno: decidir con precisión dónde termina el significado de cada palabra. En el monolito de TechCorp, "producto" es una fila de la tabla productos y todo el mundo la usa; pero lo que el catálogo entiende por producto (una ficha con descripción, imágenes y atributos) no es lo que entiende inventario (un identificador con cantidades) ni lo que entiende pedidos (un nombre y un precio congelados en el momento de la compra). Mientras las tres áreas compartían tabla, la ambigüedad se toleraba. En cuanto son servicios, la ambigüedad se convierte en acoplamiento.

Domain-Driven Design (DDD) llama a esto DDD estratégico y ofrece las herramientas para hacerlo bien: subdominios, lenguaje ubicuo, bounded contexts, mapa de contextos y agregados. Esta lección las aplica una a una a TechCorp: clasificaremos los subdominios, veremos ejemplos concretos de ambigüedad, definiremos los seis contextos y su relación con los servicios, dibujaremos el mapa de contextos con sus patrones de relación, definiremos el agregado Pedido (y por qué el stock no forma parte de él) y concretaremos qué campos de "producto" y de "cliente" guarda cada contexto. Cómo se materializa eso en bases de datos físicas separadas es el tema de 02-04.

Contenido

  1. DDD estratégico en pocas palabras
  2. Los subdominios de TechCorp: core, soporte y genéricos
  3. Lenguaje ubicuo: cuando "producto" y "cliente" no significan lo mismo
  4. Qué es un bounded context y cómo se relaciona con un microservicio
  5. El mapa de contextos y sus patrones de relación
  6. Agregados y raíces de agregado: la frontera transaccional
  7. El modelo de datos de cada contexto

  1. DDD estratégico en pocas palabras

DDD (Eric Evans, 2003) tiene dos mitades. La táctica habla de cómo escribir el código de un modelo (entidades, objetos de valor, repositorios, servicios de dominio). La estratégica habla de cómo dividir un problema grande en modelos que puedan evolucionar por separado. Para diseñar microservicios, la mitad estratégica es la que importa, y se apoya en cinco ideas:

Idea Qué es Pregunta que responde
Dominio El área de conocimiento y actividad de la empresa. Para TechCorp, "vender electrónica de consumo por internet". ¿De qué va el negocio?
Subdominio Una parte del dominio con su propia problemática. Catálogo, inventario, pedidos... ¿En qué partes se divide el problema?
Lenguaje ubicuo El vocabulario preciso, compartido por negocio y técnicos, dentro de un contexto. ¿Qué significa exactamente cada palabra aquí?
Bounded context El límite dentro del cual un modelo y su lenguaje son válidos y coherentes. ¿Hasta dónde llega este modelo?
Mapa de contextos El diagrama de cómo se relacionan los contextos y quién manda en cada relación. ¿Cómo hablan entre sí y quién se adapta a quién?

La distinción que más confusión genera: subdominio es un trozo del problema (existe aunque no haya software); bounded context es un trozo de la solución (un modelo que construimos). Lo ideal es que coincidan uno a uno, y en TechCorp así será, pero conviene tener claro que son cosas distintas.

  1. Los subdominios de TechCorp: core, soporte y genéricos

DDD clasifica los subdominios según su valor competitivo, y esa clasificación dicta cuánto esfuerzo de diseño merece cada uno:

  • Core (núcleo): donde la empresa se diferencia. Es lo que hay que diseñar con más cuidado y con el mejor equipo, y lo que nunca se compra hecho.
  • De soporte: necesario para el negocio, específico de la empresa, pero no diferenciador. Se diseña bien, sin obsesión.
  • Genérico: un problema resuelto igual en todas las empresas. Se compra, se usa un servicio externo o se implementa de la forma más sencilla posible.
Subdominio de TechCorp Tipo Justificación Consecuencia de diseño
Pedidos (ciclo de vida del pedido, flujo de compra) Core Es donde TechCorp gana o pierde ventas; la experiencia de compra y la fiabilidad del flujo la diferencian de la competencia. Equipo de Luis; modelo más rico; saga diseñada con cuidado (02-05); nunca un servicio de entidad.
Catálogo (fichas, búsqueda, precios) Core La forma de presentar y buscar productos de electrónica con atributos muy variables es parte de la propuesta de valor. Merece tecnología propia (MongoDB) y ser el primero en escalar.
Inventario (stock, reservas) Soporte Imprescindible y específico (reglas de reserva, almacenes), pero no diferencia a TechCorp. Modelo sencillo y correcto; su invariante (no vender lo que no hay) es sagrado.
Clientes (perfil, direcciones) Soporte Necesario, específico en pequeños detalles (direcciones, preferencias). Modelo simple; la autenticación, que sí es genérica, se delega.
Pagos (cobros, devoluciones) Soporte, con núcleo genérico Cobrar es genérico (lo hace la pasarela externa); cuándo y cómo cobrar según el estado del pedido es específico. Adaptador a la pasarela aislado tras una capa anticorrupción (apartado 5).
Notificaciones (correo, SMS) Genérico Enviar un correo es igual en todas partes. Implementación mínima; proveedor externo; nada de lógica de negocio dentro.
Identidad (autenticación) Genérico Login, contraseñas, tokens: problema resuelto. Se delega por completo en Keycloak (07-01); no hay servicio de TechCorp para ello.

Esta tabla justifica decisiones que ya tomamos por intuición: por qué el catálogo y los pedidos concentran la inversión, por qué la identidad no aparece en el mapa de servicios y por qué Notificaciones debe ser aburrido.

  1. Lenguaje ubicuo: cuando "producto" y "cliente" no significan lo mismo

El lenguaje ubicuo es el vocabulario que negocio y desarrollo usan sin traducción, y que aparece tal cual en el código (por eso los identificadores del curso están en español: crearPedido, PedidoRepositorio, clienteId). La observación clave de DDD es que el lenguaje ubicuo solo es coherente dentro de un contexto: la misma palabra cambia de significado al cruzar la frontera, y pretender un único modelo "producto" para toda la empresa produce un modelo gigante que no sirve bien a nadie (es la tabla productos + atributos_producto de TechCorp).

3.1 "Producto"

Contexto Qué es un "producto" Atributos que le importan Qué le da igual
Catálogo Una ficha comercial que se muestra y se busca. Nombre, descripción larga, categoría, imágenes, atributos variables (voltaje, compatibilidad, color), precio de venta actual, si está publicado. Cuánto stock hay; en qué pedidos aparece.
Inventario Una referencia almacenable de la que hay unidades. productoId, cantidad física, cantidad reservada, ubicación en almacén, umbral de reposición. La descripción, las imágenes, el precio.
Pedidos Una línea de pedido: lo que el cliente compró, al precio al que lo compró. productoId, nombre en el momento de la compra, precio unitario en el momento de la compra, cantidad. Que el precio haya cambiado después; el stock actual; los atributos.

La consecuencia práctica más importante está en la tercera fila: si el catálogo cambia el precio de p-501 mañana, el pedido ped-88213 de hoy no debe cambiar. Por eso pedidos guarda una copia congelada de nombre y precio en lineas_pedido, y por eso el evento "Precio cambiado" del event storming de 02-02 no le interesa. No es duplicación accidental: es que son dos conceptos distintos que comparten identificador.

3.2 "Cliente"

Contexto Qué es un "cliente" Atributos que le importan
Clientes Una persona registrada con perfil y direcciones. clienteId, email, nombre, direcciones guardadas, preferencias de contacto, fecha de alta, identificador en Keycloak.
Pedidos Quien hace el pedido y a dónde se envía. clienteId (referencia), dirección de envío de este pedido (copia; si el cliente cambia su dirección después, el pedido en curso no debe moverse).
Notificaciones Un destinatario. Email y nombre para saludar. Nada más. Y los recibe en cada evento, no los almacena como maestro.
Pagos Casi no existe: un pago se asocia a un pedido, no a un cliente. pedidoId, importe. El "titular" lo conoce la pasarela, no TechCorp.

3.3 Otras palabras con doble sentido

  • "Estado": en Pedidos, PENDIENTE / CONFIRMADO / CANCELADO; en Pagos, AUTORIZADO / CAPTURADO / REEMBOLSADO; en Inventario, una reserva está ACTIVA / CONSUMIDA / LIBERADA. Tres máquinas de estados distintas que hoy conviven en columnas estado TEXT sin relación.
  • "Cancelar": para Pedidos es cerrar el pedido sin completarlo; para Pagos es no capturar un cobro autorizado; para Inventario es liberar una reserva. La saga de 02-05 encadenará las tres, pero cada contexto usa su verbo.
  • "Disponible": en Catálogo significa publicado; en Inventario significa cantidad - reservado > 0. Un producto puede estar disponible en catálogo y no en inventario, y viceversa.

Detectar estas colisiones es, en la práctica, la forma más fiable de encontrar fronteras: allí donde una palabra cambia de significado, hay una costura.

  1. Qué es un bounded context y cómo se relaciona con un microservicio

Un bounded context es el límite explícito dentro del cual un modelo de dominio y su lenguaje ubicuo son coherentes. Dentro, "producto" significa una sola cosa; fuera, puede significar otra. Cada contexto tiene su propio modelo, su propio código y (como veremos en 02-04) sus propios datos.

La relación con los microservicios:

Relación Cuándo Ejemplo Valoración
1 contexto = 1 servicio Lo habitual y lo recomendable de partida. Los seis contextos de TechCorp → los seis servicios del mapa objetivo. Máxima claridad: la frontera del modelo es la frontera de despliegue y de datos.
1 contexto = varios servicios Cuando dentro de un contexto hay partes con escalado o ritmo de cambio muy distinto. Si la búsqueda del catálogo necesitara un motor de indexación separado de la gestión de fichas, ambos seguirían hablando el mismo lenguaje ("ficha", "atributo") pero serían dos despliegues. Aceptable; los dos servicios comparten lenguaje pero no base de datos ni código de negocio.
Varios contextos = 1 servicio En etapas tempranas o en dominios pequeños. El monolito modular de 01-03; o el "monolito residual" de TechCorp mientras Pedidos y Clientes aún no se han extraído. Válido como paso intermedio si los contextos están separados como módulos y no comparten tablas por diseño.
1 servicio abarca medio contexto Nunca. Un servicio-lineas-pedido separado de servicio-pedidos. Rompe el modelo por la mitad: los invariantes del pedido (apartado 6) quedan repartidos en dos procesos.

La regla de TechCorp: un contexto, un servicio, un equipo dueño. Y la excepción admitida: durante la migración, el monolito residual contiene varios contextos, cada uno en su módulo, con las abstracciones de 02-02 marcando dónde están las fronteras.

  1. El mapa de contextos y sus patrones de relación

Los contextos no viven aislados: Pedidos necesita precios de Catálogo, Notificaciones necesita saber que un pedido se ha confirmado. El mapa de contextos dibuja esas relaciones y, sobre todo, quién manda en cada una: en cualquier relación hay un contexto upstream (aguas arriba: su modelo influye) y uno downstream (aguas abajo: se adapta o se protege). DDD cataloga los patrones de relación:

Patrón Qué significa Quién manda Cuándo se usa
Customer-Supplier (cliente-proveedor) El upstream (proveedor) atiende necesidades del downstream (cliente); hay negociación: el cliente puede pedir campos, el proveedor planifica. Upstream, pero escucha. Equipos que colaboran de buena fe.
Conformist (conformista) El downstream acepta el modelo del upstream tal cual, sin traducirlo ni influir en él. Upstream por completo. Cuando el upstream no va a cambiar por ti (o no merece la pena traducir).
Anticorruption Layer, ACL (capa anticorrupción) El downstream pone una capa que traduce el modelo del upstream a su propio lenguaje, para que el modelo ajeno no "contamine" el suyo. Upstream, pero el downstream se protege. Sistemas externos, sistemas heredados, modelos muy distintos.
Shared Kernel (núcleo compartido) Dos contextos comparten un trozo de modelo (código o esquema) y acuerdan cambiarlo juntos. Ambos, de mutuo acuerdo. Solo entre equipos muy cercanos; es acoplamiento asumido.
Open Host Service, OHS (servicio de host abierto) El upstream ofrece un protocolo público y estable para que cualquiera lo consuma, en vez de integraciones a medida. Upstream define el protocolo. Contextos con muchos consumidores.
Published Language, PL (lenguaje publicado) El formato de intercambio está documentado y versionado (esquema JSON, contrato de eventos). Suele acompañar a OHS. Upstream publica; todos lo entienden. Casi siempre, junto a OHS o a eventos.
Partnership (asociación) Dos contextos evolucionan juntos por necesidad mutua, coordinando planes. Ninguno; iguales. Contextos del mismo equipo o con dependencia bidireccional fuerte.
Separate Ways (caminos separados) No hay relación; si hiciera falta algo, se duplica. — Cuando integrar cuesta más que no integrar.

Aplicado a TechCorp:

flowchart LR
    CAT[Catálogo<br/>OHS + PL]
    CLI[Clientes<br/>OHS + PL]
    PED[Pedidos<br/>core]
    INV[Inventario]
    PAG[Pagos]
    NOT[Notificaciones]
    KC[Keycloak<br/>externo]
    PAS[Pasarela de pago<br/>externa]

    CAT -- "U: precios y nombres<br/>D: ACL 'traductorProducto'" --> PED
    CLI -- "U: existencia, email, dirección<br/>D: Customer-Supplier" --> PED
    PED <-->|"Partnership<br/>eventos pedido.creado / stock.reservado"| INV
    PED -- "U: eventos (PL)<br/>D: Customer-Supplier" --> PAG
    PAG -- "U: pago.confirmado (PL)" --> PED
    PED -- "U: pedido.confirmado / cancelado (PL)<br/>D: Conformist" --> NOT
    KC -- "U: tokens OIDC<br/>D: Conformist" --> CLI
    PAS -- "U: API propietaria<br/>D: ACL" --> PAG

Lectura relación por relación (U = upstream, D = downstream):

  • Catálogo → Pedidos: OHS + PL con ACL en Pedidos. Catálogo publica un contrato abierto (GET /productos?ids=..., formato JSON documentado) para todos los consumidores (Pedidos, la web, futuros servicios). Pedidos no incorpora el modelo "ficha de catálogo" a su dominio: una pequeña capa (traductorProducto) convierte { productoId, nombre, precio, descripcion, atributos... } en lo único que Pedidos entiende, una LineaPedido con nombre y precio congelados. Así, si Catálogo cambia sus atributos, Pedidos no se entera.
  • Clientes → Pedidos: Customer-Supplier. Pedidos necesita saber que el cliente existe y obtener email y dirección de envío. Es una relación negociada: el equipo de Pedidos puede pedir a Clientes que exponga un GET /clientes/{id} con exactamente esos campos.
  • Pedidos ↔ Inventario: Partnership. Ambos son del equipo de Luis y evolucionan juntos: Pedidos publica pedido.creado, Inventario responde con stock.reservado (o su negativa). Los contratos de esos eventos se diseñan en la misma mesa. Nótese que Partnership no significa base de datos compartida: significa planificación compartida.
  • Pedidos → Pagos → Pedidos: Customer-Supplier por eventos, con PL. Pagos consume stock.reservado y publica pago.confirmado (o su rechazo). El lenguaje publicado son los esquemas de esos eventos.
  • Pedidos → Notificaciones: Conformist. Notificaciones acepta los eventos de Pedidos tal cual, sin pedir cambios: es un subdominio genérico y su trabajo es reaccionar. Si pedido.confirmado trae email y nombre, los usa; no tiene modelo propio de cliente que proteger.
  • Keycloak → Clientes: Conformist. Keycloak es externo y estándar (OpenID Connect); Clientes se adapta a sus tokens y guarda el identificador de Keycloak (sub) junto al clienteId.
  • Pasarela → Pagos: ACL. La pasarela tiene su propia API, sus propios estados y sus propios errores. servicio-pagos la envuelve en un adaptador (servicios/pasarelaPago.js, heredado del monolito y ahora en exclusiva) que traduce a los conceptos de Pagos: AUTORIZADO, CAPTURADO, RECHAZADO, REEMBOLSADO. Cambiar de pasarela solo toca esa capa.
  • Shared Kernel: ninguno, por decisión. La tentación sería compartir "identificadores", "dinero" o "el sobre de los eventos" como librería común de dominio. TechCorp lo evita: los identificadores son cadenas opacas y el formato del sobre de eventos es lenguaje publicado (documentado), no código compartido. Es la misma regla de 02-02 sobre utilidades compartidas.
  • Separate Ways: Catálogo y Notificaciones, Inventario y Clientes. No se hablan. Si Notificaciones algún día necesitara el nombre de un producto para un correo, lo recibiría en el evento antes que integrarse con Catálogo.

  1. Agregados y raíces de agregado: la frontera transaccional

Bajamos un nivel. Dentro de un contexto, el modelo se organiza en agregados: grupos de objetos que se tratan como una unidad para cambios de estado. Cada agregado tiene una raíz (la única entidad a la que se accede desde fuera) y define una frontera de consistencia: los invariantes (reglas que siempre deben cumplirse) se garantizan dentro del agregado, en una única transacción; entre agregados, la consistencia es eventual.

Reglas prácticas de diseño de agregados:

  1. Una transacción modifica un solo agregado. Si necesitas modificar dos, o el diseño está mal o toca aceptar consistencia eventual entre ellos (02-05).
  2. Los agregados se referencian por identificador, nunca por objeto: un Pedido guarda clienteId, no un objeto Cliente.
  3. Agregados pequeños. Un agregado grande bloquea mucho y escala mal.
  4. La raíz protege los invariantes. Nadie modifica una línea de pedido "por fuera"; se pide al Pedido que la añada, y el Pedido comprueba que puede.

6.1 El agregado Pedido

En el contexto Pedidos, el agregado es Pedido (raíz) con sus LíneaPedido (entidades internas) y su DirecciónEnvío (objeto de valor, copia). Sus invariantes:

  • El total es siempre la suma de precioUnitario × cantidad de sus líneas.
  • Un pedido tiene al menos una línea y ninguna línea tiene cantidad ≤ 0.
  • Un pedido CONFIRMADO o CANCELADO no admite cambios de líneas.
  • Las transiciones de estado siguen la máquina de estados que veremos en 02-05.
// dominio/Pedido.js  (contexto Pedidos) - esqueleto ilustrativo del agregado, sin persistencia
class Pedido {
  #lineas = [];                                  // solo la raíz toca las líneas
  constructor({ pedidoId, clienteId, direccionEnvio }) {
    this.pedidoId = pedidoId;                    // 'ped-88213'
    this.clienteId = clienteId;                  // referencia por id: NO un objeto Cliente
    this.direccionEnvio = { ...direccionEnvio }; // copia congelada (objeto de valor)
    this.estado = 'PENDIENTE';
  }

  anadirLinea({ productoId, nombre, precioUnitario, cantidad }) {
    if (this.estado !== 'PENDIENTE') throw new Error('PEDIDO_NO_MODIFICABLE');
    if (cantidad <= 0) throw new Error('CANTIDAD_INVALIDA');
    // nombre y precioUnitario llegan ya traducidos desde Catálogo (ACL) y se congelan aquí
    this.#lineas.push({ productoId, nombre, precioUnitario, cantidad });
  }

  get total() {                                  // invariante: siempre derivado de las líneas
    return this.#lineas.reduce((suma, l) => suma + l.precioUnitario * l.cantidad, 0);
  }

  get lineas() { return this.#lineas.map(l => ({ ...l })); }   // copia: nadie modifica por fuera
}
module.exports = { Pedido };

Qué no hay en este agregado, y por qué:

  • No hay stock. El invariante "no reservar más de lo que hay" (cantidad - reservado >= 0) pertenece al contexto Inventario y a su agregado (una Reserva asociada a un pedidoId, o el propio Stock por producto). Meter el stock en el Pedido tendría tres problemas: (1) el Pedido debería conocer un invariante que no es suyo; (2) una fila de stock la tocan cientos de pedidos concurrentes, así que el agregado Pedido "abarcaría" filas compartidas por todos los pedidos y las transacciones se bloquearían entre sí (exactamente lo que pasaba en crearPedido con la pasarela dentro de la transacción); (3) el stock cambia por motivos ajenos al pedido (entradas de almacén, mermas). Por eso, en el diseño objetivo, Pedidos pide la reserva y la respuesta llega como evento.
  • No hay objeto Cliente ni objeto Producto. Solo clienteId y productoId, más las copias de valor (dirección, nombre, precio) que el pedido necesita para ser autosuficiente.
  • No hay pago. El Pago es agregado de otro contexto; el Pedido solo conoce que su estado pasó a PAGADO cuando llegó el evento.

Y en los otros contextos, los agregados principales:

Contexto Agregado (raíz) Contiene Invariante principal
Pedidos Pedido Líneas, dirección de envío, estado, total Total = suma de líneas; transiciones válidas.
Inventario Stock por producto y Reserva Cantidad, reservado; reserva: pedidoId, líneas, estado cantidad - reservado >= 0; una reserva no se consume dos veces.
Catálogo Producto (ficha) Descripción, atributos, imágenes, precio actual, publicado Un producto publicado tiene nombre y precio.
Pagos Pago pedidoId, importe, estado, referencia de la pasarela No capturar dos veces el mismo pedido.
Clientes Cliente Perfil, direcciones Email único.
Notificaciones Envío (mínimo) Destinatario, plantilla, estado No reenviar el mismo aviso.

  1. El modelo de datos de cada contexto

Concretemos qué guarda cada contexto de los conceptos compartidos. Todavía no hablamos de bases de datos físicas (02-04), sino de qué campos existen en cada modelo.

7.1 "Producto" en los tres contextos

Campo Catálogo Inventario Pedidos (lineas_pedido)
productoId Sí (lo genera) Sí (referencia) Sí (referencia)
nombre Sí (maestro) No Sí, copia congelada
descripcion, imagenes, categoria Sí No No
atributos variables (voltaje, color...) Sí (documento flexible) No No
precio Sí (precio actual) No Sí, copia congelada como precio_unitario
publicado / activo Sí No No
cantidad, reservado No Sí (maestro) No
ubicacion, umbral_reposicion No Sí No
cantidad pedida No En la reserva Sí

Ejemplo del mismo producto visto desde cada contexto (formato ilustrativo; el esquema físico llega en 02-04):

// Catálogo: la ficha (documento flexible)
{ "productoId": "p-501", "nombre": "Auriculares BT X200", "precio": 59.90,
  "categoria": "audio", "publicado": true,
  "atributos": { "color": "negro", "autonomiaHoras": 30, "bluetooth": "5.3" },
  "imagenes": ["x200-frontal.webp", "x200-lateral.webp"] }

// Inventario: la referencia almacenable
{ "productoId": "p-501", "cantidad": 120, "reservado": 7, "ubicacion": "A-03-2", "umbralReposicion": 20 }

// Pedidos: la línea del pedido ped-88213 (precio del día de la compra, aunque mañana cambie)
{ "productoId": "p-501", "nombre": "Auriculares BT X200", "precioUnitario": 59.90, "cantidad": 1 }

7.2 "Cliente" en los contextos que lo usan

Campo Clientes Pedidos Notificaciones
clienteId Sí (lo genera) Sí (referencia) Solo si viene en el evento
email, nombre Sí (maestro) No los almacena como maestro; los obtiene por contrato al crear el pedido y los incluye en los eventos Los recibe en cada evento; no los guarda como maestro
direcciones[] guardadas Sí No No
direccionEnvio del pedido No Sí, copia congelada por pedido No
keycloakSub, preferencias, fecha de alta Sí No No

7.3 El pedido completo tal como lo ve su contexto

{
  "pedidoId": "ped-88213",
  "clienteId": "c-1024",
  "estado": "PENDIENTE",
  "direccionEnvio": { "calle": "Gran Vía 12", "cp": "28013", "ciudad": "Madrid" },
  "lineas": [
    { "productoId": "p-501", "nombre": "Auriculares BT X200", "precioUnitario": 59.90, "cantidad": 1 },
    { "productoId": "p-777", "nombre": "Cable USB-C 2 m",     "precioUnitario": 9.90,  "cantidad": 2 }
  ],
  "total": 79.70,
  "creadoEn": "2026-08-15T10:42:00Z"
}

Es la misma estructura que devolvía GET /pedidos/{id} en 01-01, ahora explicada: todo lo que hay dentro es del contexto Pedidos (referencias por id + copias de valor); nada de lo que hay dentro obliga a consultar otro contexto para leer el pedido.

Errores Comunes y Consejos

  • Buscar el "modelo canónico" de producto para toda la empresa. Es el camino de vuelta a la tabla productos + atributos_producto. Cada contexto tiene su producto; comparten identificador, no modelo.
  • Confundir bounded context con módulo técnico. "Contexto de persistencia" o "contexto de API" no son bounded contexts: no tienen lenguaje de negocio propio.
  • Agregados enormes. Un Pedido que contiene el Cliente, los Productos y el Stock "para tenerlo todo a mano" bloquea a media empresa en cada transacción. Referencias por id y copias de valor.
  • Copiar datos "por si acaso" en lugar de por semántica. Pedidos copia nombre y precio porque el negocio dice que el pedido conserva el precio de la compra, no por rendimiento. Si copias algo que semánticamente debe estar siempre actualizado (por ejemplo, el email para contactar), tendrás datos obsoletos; ahí toca consultar o suscribirse a cambios (02-04).
  • Ignorar el ACL con sistemas externos. Dejar que los estados de la pasarela de pago se filtren en el modelo de Pagos (y de ahí a los eventos) hace que cambiar de pasarela sea un cambio en toda la empresa.
  • Consejo: mantén un glosario por contexto (una tabla: término, significado en este contexto, campo en el código). Cuando dos glosarios discrepen sobre una palabra, no lo resuelvas: es una frontera confirmada.

Ejercicios

Ejercicio 1: Clasificar subdominios y elegir patrón

TechCorp estudia añadir dos capacidades: (a) valoraciones de productos por los clientes (estrellas y comentarios en la ficha) y (b) facturación (emisión de facturas legales por cada pedido confirmado). Para cada una: clasifícala como core, soporte o genérica; indica de qué contextos existentes sería downstream y qué patrón de relación usarías con cada uno.

Ejercicio 2: Detectar la palabra ambigua

En una reunión, el equipo de Inventario dice "el producto p-501 no está disponible" y el equipo de Catálogo responde "sí que lo está, lo publiqué ayer". Explica la ambigüedad con el vocabulario de esta lección, indica cómo la resolverías en los contratos (qué campo expone cada contexto) y qué debería mostrar la web al cliente en ese caso.

Ejercicio 3: ¿Dentro o fuera del agregado?

Para cada elemento, decide si forma parte del agregado Pedido, es un agregado propio del contexto Pedidos, o pertenece a otro contexto, y justifica con la regla de invariantes: (1) el cupón de descuento aplicado a un pedido; (2) el historial de cambios de estado del pedido; (3) la valoración que el cliente deja del pedido tras recibirlo; (4) la reserva de stock del pedido.

Soluciones

Ejercicio 1

(a) Valoraciones: subdominio de soporte (aporta a la experiencia, pero no es el núcleo de venta ni un problema genérico). Sería downstream de Catálogo (necesita saber que el producto existe: OHS + PL, consumiendo el contrato público) y de Pedidos o Clientes para verificar que quien valora compró el producto (Customer-Supplier: pediría un GET /pedidos?clienteId=...&productoId=... o un evento pedido.entregado). Su modelo de "producto" sería mínimo: productoId y nombre para mostrar. Podría vivir dentro del contexto Catálogo si es muy simple; como servicio propio si el volumen de escritura o el equipo lo justifican. (b) Facturación: subdominio de soporte con fuerte componente genérico (las reglas fiscales son iguales para todos; muchas empresas usan un proveedor). Downstream de Pedidos (Conformist o Customer-Supplier sobre pedido.confirmado, del que necesita líneas, importes e impuestos) y de Clientes (datos fiscales: Customer-Supplier, pidiendo un campo datosFiscales). Si se usa un proveedor externo de facturación, ACL delante de su API. Nunca compartiría tablas con Pedidos aunque "casi todo lo que necesita está en pedidos".

Ejercicio 2

"Disponible" tiene dos significados: en Catálogo, publicado (la ficha se muestra); en Inventario, con unidades libres (cantidad - reservado > 0). Ambos tienen razón en su contexto. En los contratos, cada uno expone su concepto con su nombre: Catálogo, publicado: true/false; Inventario, disponible: 113 (unidades libres) o hayStock: true/false. La web (o un BFF, 03-04) compone ambos: muestra la ficha porque está publicada y el botón "comprar" desactivado con el texto "sin stock" porque Inventario dice 0. Lo que no debe hacerse es añadir a la ficha del catálogo un campo stock mantenido a mano: es un dato de otro contexto (si se muestra, se obtiene por contrato o por réplica de eventos, 02-04).

Ejercicio 3

  1. Cupón aplicado: dentro del agregado Pedido como dato de valor (descuentoAplicado, código y cantidad), porque afecta al invariante del total; la definición del cupón (validez, condiciones) es de otro contexto (Promociones), referenciado por código.
  2. Historial de estados: puede ser parte del agregado (lista de transiciones con fecha) si es pequeño y se usa para validar transiciones; si crece o solo sirve para auditoría, agregado propio o simplemente un registro de eventos del contexto Pedidos. En ningún caso otro contexto.
  3. Valoración del pedido: fuera del agregado y probablemente fuera del contexto: no afecta a ningún invariante del pedido y tiene ciclo de vida propio (llega días después, se puede editar). Referencia por pedidoId.
  4. Reserva de stock: otro contexto (Inventario), agregado Reserva con pedidoId. Su invariante (cantidad - reservado >= 0) no es del Pedido, y meterla dentro haría que cada pedido bloquease filas compartidas por todos.

Conclusión

Con DDD estratégico hemos convertido las seis áreas de TechCorp en seis bounded contexts bien definidos: hemos clasificado sus subdominios (Pedidos y Catálogo como core; Inventario, Clientes y Pagos de soporte; Notificaciones e Identidad genéricos, esta última delegada en Keycloak), hemos comprobado que "producto", "cliente", "estado", "cancelar" y "disponible" significan cosas distintas en cada contexto y que esa diferencia marca las costuras, hemos dibujado el mapa de contextos con sus patrones (Catálogo como OHS + PL con ACL en Pedidos; Clientes → Pedidos como Customer-Supplier; Pedidos ↔ Inventario como Partnership; Pedidos → Notificaciones como Conformist; ACL frente a la pasarela; sin shared kernel por decisión) y hemos definido el agregado Pedido con sus líneas y su dirección congelada, dejando fuera el stock, el cliente y el pago por razones de invariantes y contención. Finalmente, hemos fijado qué campos guarda cada contexto de los conceptos compartidos: el catálogo tiene la ficha completa; inventario, productoId y cantidades; pedidos, copias congeladas de nombre y precio en sus líneas.

Todo esto sigue siendo modelo. La siguiente lección lo hace físico: una base de datos por servicio. Veremos por qué la base de datos compartida es el antipatrón principal, cómo se rompen las claves foráneas cruzadas del esquema de 01-05 (pedidos.cliente_id, lineas_pedido.producto_id), qué alternativas hay para las consultas que hoy son un JOIN, cómo se migran los datos sin parar la tienda y qué esquema concreto tendrá cada servicio, incluido el catálogo en MongoDB.

Curso de Microservicios

Módulo 1: Introducción a los Microservicios

Módulo 2: Diseño de Microservicios

Módulo 3: Comunicación entre Microservicios

Módulo 4: Implementación de Microservicios

Módulo 5: Despliegue y Orquestación

Módulo 6: Monitoreo y Mantenimiento

Módulo 7: Seguridad en Microservicios

Módulo 8: Casos de Estudio y Ejemplos Prácticos

© Copyright 2026. Todos los derechos reservados