Diseño basado en dominios (DDD) en microservicios

Diseño basado en dominios (DDD) en microservicios

Cuando las organizaciones pasan de una arquitectura monolítica a microservicios, se enfrentan a una pregunta crítica y de alto riesgo: ¿Cómo trazamos los límites de nuestros servicios?

En teoría, los microservicios deberían ser unidades flexibles y desacopladas que puedan desarrollarse, implementarse y escalarse de forma independiente. Sin embargo, en la práctica, muchos equipos terminan construyendo un monolito distribuido: un sistema donde los servicios están tan estrechamente acoplados que un único cambio empresarial requiere modificar e implementar múltiples servicios simultáneamente, lo que agrava la latencia de la red y los bloqueos en la implementación.

Para evitar este problema, los arquitectos de software recurren al Diseño basado en dominios (DDD). Introducido por primera vez por Eric Evans en 2003, DDD es una metodología de desarrollo de software que alinea las estructuras de código con los complejos dominios comerciales que representan.

En este artículo, exploraremos cómo DDD proporciona los planos estratégicos y tácticos para diseñar microservicios limpios, desacoplados y altamente mantenibles.


1. El plan estratégico: trazar los límites del servicio

DDD se divide en dos fases principales: Diseño estratégico y Diseño táctico. El diseño estratégico consiste en modelar contextos comerciales y definir límites de servicios. Proporciona la vista “macro” de la arquitectura.

Mapeo de contextos delimitados DDD al diagrama de arquitectura de microservicios

A. Lenguaje ubicuo

Dentro de una empresa, diferentes departamentos utilizan las mismas palabras para significar cosas completamente diferentes. Por ejemplo:

  • Para el equipo de ventas, un “Usuario” es un cliente potencial.
  • Para el equipo de seguridad, un “Usuario” es un conjunto de credenciales de inicio de sesión.
  • Para el equipo de envío, un “Usuario” es una dirección física del destinatario.

Intentar crear un modelo de base de datos único y unificado para un “usuario” que satisfaga a todos da como resultado una base de código masiva y enredada. DDD resuelve esto estableciendo un lenguaje ubicuo: un vocabulario compartido definido y utilizado tanto por expertos en el ámbito empresarial como por desarrolladores dentro de un límite específico.

B. Contextos acotados

Un Contexto delimitado es el límite explícito dentro del cual se aplica un modelo de dominio. Dentro de la frontera, todos los términos del lenguaje ubicuo tienen un significado único e inequívoco.

En lugar de un único modelo de “Usuario”, definimos modelos separados dentro de sus respectivos contextos delimitados:

  • En el Contexto del pedido, tenemos un modelo Order que contiene información de contacto del cliente.
  • En el Contexto de identidad, tenemos un modelo Credentials.
  • En el Contexto de envío, tenemos un modelo DeliveryAddress.

La regla de oro de los microservicios: Un contexto delimitado se asigna directamente a un microservicio.

Al dividir el sistema en contextos acotados, nos aseguramos de que los microservicios estén débilmente acoplados y representen capacidades comerciales distintas.

C. Mapeo de contexto: cómo se comunican los servicios

Los contextos acotados no existen de forma aislada; deben interactuar. Un Mapa de contexto define las relaciones y los mecanismos de traducción entre contextos. Los patrones clave incluyen:

  • Kernel compartido: dos contextos comparten un pequeño subconjunto del modelo de dominio y la base de datos (normalmente no se recomienda en microservicios debido al acoplamiento).
  • Cliente-Proveedor: Un contexto (proveedor) debe proporcionar datos a otro (cliente). El proveedor debe coordinar los cronogramas de liberación con el cliente.
  • Capa anticorrupción (ACL): una capa de traducción que traduce los datos entrantes de un sistema externo al modelo de dominio interno del cliente, evitando que los modelos externos contaminen la arquitectura interna.

2. El plan táctico: estructuración del código base de microservicios

Una vez que se trazan los límites del servicio mediante el diseño estratégico, Tactical Design proporciona un conjunto de patrones de diseño para estructurar el código dentro de un único microservicio.

A. Entidades y objetos de valor

Dentro de un microservicio, los modelos se dividen en dos categorías:

  1. Entidades: Objetos que tienen una identidad única que persiste en el tiempo, incluso si sus atributos cambian. Los ejemplos incluyen Order o Product.
  2. Objetos de valor: Objetos que no tienen una identidad única y están definidos completamente por sus atributos. Son inmutables. Los ejemplos incluyen ShippingAddress o ProductDimensions. Si dos objetos de valor tienen los mismos atributos, se consideran iguales.

B. Agregados y raíces agregadas

Un Agregado es un grupo de entidades y objetos de valor asociados que se tratan como una sola unidad para los cambios de datos. Cada agregado tiene una Raíz de agregado, que es el único punto de entrada a través del cual los objetos externos pueden interactuar con el agregado. La raíz garantiza que se apliquen todas las invariantes (reglas) comerciales.

Por ejemplo, en el diagrama de arriba:

  • En el Servicio de pedidos, el Order es la raíz agregada. Contiene OrderItem (Entidad) y ShippingAddress (Objeto de valor). Los servicios externos no pueden modificar OrderItem directamente; deben llamar a un método en la raíz Order (por ejemplo, order.AddItem()), que valida que el pedido aún no se haya enviado.

C. Eventos de dominio

Un Evento de Dominio es algo que sucedió en el dominio que interesa a los expertos en negocios. Se utiliza para comunicar cambios en contextos acotados de forma asincrónica. Cuando un Agregado ejecuta un comando, publica un Evento de Dominio (por ejemplo, OrderCreated). Otros servicios escuchan este evento y actualizan su estado en consecuencia, lo que garantiza una coherencia final sin dependencias de API sincrónicas.


3. Un ejemplo concreto: servicio de pedidos versus servicio de inventario

Veamos una implementación simplificada de DDD táctico en Go para el Servicio de pedidos, que muestra cómo Aggregate Root coordina las reglas comerciales.

package domain

import (
	"errors"
	"time"
)

// Value Object (Immutable, identity-less)
type ShippingAddress struct {
	Street  string
	City    string
	ZipCode string
}

// Entity (Has identity, mutable)
type OrderItem struct {
	ProductID string
	Quantity  int
	Price     float64
}

// Aggregate Root (Enforces transactional boundaries)
type Order struct {
	ID        string
	Items     []OrderItem
	Address   ShippingAddress
	Status    string
	CreatedAt time.Time
}

// NewOrder creates a new Order Aggregate Root
func NewOrder(id string, address ShippingAddress) *Order {
	return &Order{
		ID:        id,
		Items:     []OrderItem{},
		Address:   address,
		Status:    "PENDING",
		CreatedAt: time.Now(),
	}
}

// AddItem enforces business rules before mutating state
func (o *Order) AddItem(productID string, qty int, price float64) error {
	if o.Status != "PENDING" {
		return errors.New("cannot add items to a finalized or cancelled order")
	}
	if qty <= 0 {
		return errors.New("quantity must be greater than zero")
	}
	
	o.Items = append(o.Items, OrderItem{
		ProductID: productID,
		Quantity:  qty,
		Price:     price,
	})
	return nil
}

Cuando la orden se guarda correctamente, el servicio publica un evento en un intermediario de mensajes:

{
  "event_id": "evt_98231",
  "event_type": "OrderCreated",
  "timestamp": "2026-06-26T00:15:00Z",
  "payload": {
    "order_id": "ord_5521",
    "items": [
      { "product_id": "prod_88", "quantity": 2 }
    ]
  }
}

El Servicio de inventario escucha este evento, actualiza su entidad StockLevel local y completa el flujo de forma asincrónica.


4. DDD estratégico versus táctico en microservicios

Fase / Aspecto DDD estratégica DDD táctico
Alcance Arquitectura del sistema global (macro) Dentro de una base de código de servicio único (micro)
Objetivo principal Trazar límites de servicios limpios y desacoplados Modele una lógica empresarial rica y aplique invariantes
Conceptos clave Contextos acotados, lenguaje ubicuo, mapa de contexto Entidades, objetos de valor, agregados, eventos de dominio
Audiencia Arquitectos, gerentes de producto, desarrolladores Desarrolladores de software, revisores de código
Impacto en los Microservicios Determina el número y alcance de los servicios Determina las transacciones de la base de datos y la estructura del directorio

Conclusión: Comience primero con DDD estratégico

Aplicar el diseño basado en dominios a los microservicios es una forma poderosa de garantizar que su arquitectura coincida con su estructura empresarial. Sin embargo, los equipos a menudo cometen el error de centrarse demasiado en patrones tácticos (como escribir interfaces de repositorio y objetos de valor) mientras ignoran el diseño estratégico.

Si no establece los límites correctos, ninguna cantidad de código táctico limpio evitará que su sistema se convierta en un monolito distribuido. Comience siempre con un mapeo estratégico: defina su lenguaje ubicuo, agrupe la lógica en contextos acotados, mapee los flujos de comunicación y deje que los límites de sus microservicios surjan naturalmente de esas definiciones.


Explore más arquitectura de software, patrones de diseño y conocimientos de ingeniería en el Blog de Ghaznix →