Introducción a la clase
Hemos aprendido cómo funciona Clean Architecture y sus capas. Ahora, veremos cómo integrar Domain-Driven Design (DDD) dentro de esta arquitectura, lo que nos permitirá modelar mejor el dominio de la aplicación, haciendo que el código sea más expresivo, mantenible y flexible.
¿Qué obtendrás de esta clase?
- Aprenderás cómo combinar Clean Architecture con DDD de manera efectiva.
- Comprenderás dónde colocar Entidades, Value Objects y Agregados en Clean Architecture.
- Implementarás repositorios y servicios de dominio según DDD.
- Verás ejemplos en C# con una estructura realista.
Cómo DDD encaja en Clean Architecture
Clean Architecture proporciona la estructura de capas, pero DDD define cómo modelar el dominio dentro de esa estructura.
- Las Entidades y Value Objects se ubican en la Capa de Dominio.
- Los Agregados ayudan a encapsular y proteger la consistencia del dominio.
- Los Casos de Uso en la Capa de Aplicación manipulan los Agregados sin exponer detalles internos.
- Los Repositorios actúan como puentes entre el dominio y la infraestructura.
![](https://i2.wp.com/withoutdebugger.com/wp-content/uploads/2025/02/image-1.png?resize=640%2C202&ssl=1)
Ubicación de los elementos de DDD en Clean Architecture
Concepto DDD | Capa en Clean Architecture | Propósito |
---|---|---|
Entidades | Dominio | Representan objetos con identidad única y lógica de negocio |
Value Objects | Dominio | Representan conceptos sin identidad que son inmutables |
Agregados | Dominio | Grupo de Entidades y Value Objects que deben mantenerse consistentes |
Casos de Uso | Aplicación | Orquestan la ejecución de reglas de negocio en el dominio |
Repositorios | Infraestructura | Implementan la persistencia de los Agregados en la base de datos |
Ejemplo en C#: Implementación de DDD en Clean Architecture
1. Entidad «Pedido» en la Capa de Dominio
public class Pedido
{
public int Id { get; private set; }
public string Cliente { get; private set; }
private readonly List<Producto> _productos = new List<Producto>();
public IReadOnlyCollection<Producto> Productos => _productos.AsReadOnly();
public Pedido(int id, string cliente)
{
Id = id;
Cliente = cliente;
}
public void AgregarProducto(Producto producto)
{
_productos.Add(producto);
}
public decimal CalcularTotal()
{
return _productos.Sum(p => p.Precio);
}
}
2. Value Object «Dirección» en la Capa de Dominio
public class Direccion
{
public string Calle { get; }
public string Ciudad { get; }
public string CodigoPostal { get; }
public Direccion(string calle, string ciudad, string codigoPostal)
{
Calle = calle;
Ciudad = ciudad;
CodigoPostal = codigoPostal;
}
public override bool Equals(object obj)
{
if (obj is Direccion other)
{
return Calle == other.Calle && Ciudad == other.Ciudad && CodigoPostal == other.CodigoPostal;
}
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(Calle, Ciudad, CodigoPostal);
}
}
3. Agregado «Pedido» que agrupa Productos
public class Producto
{
public int Id { get; private set; }
public string Nombre { get; private set; }
public decimal Precio { get; private set; }
public Producto(int id, string nombre, decimal precio)
{
Id = id;
Nombre = nombre;
Precio = precio;
}
}
4. Caso de Uso en la Capa de Aplicación
public class CrearPedidoUseCase
{
private readonly IPedidoRepository _repository;
public CrearPedidoUseCase(IPedidoRepository repository)
{
_repository = repository;
}
public void Ejecutar(Pedido pedido)
{
_repository.Guardar(pedido);
}
}
5. Repositorio en la Capa de Infraestructura
public class PedidoRepository : IPedidoRepository
{
private readonly List<Pedido> _pedidos = new List<Pedido>();
public void Guardar(Pedido pedido)
{
_pedidos.Add(pedido);
Console.WriteLine("Pedido guardado en memoria.");
}
public Pedido ObtenerPorId(int id)
{
return _pedidos.FirstOrDefault(p => p.Id == id);
}
}
6. Controlador en la Capa de Presentación
[ApiController]
[Route("api/pedidos")]
public class PedidoController : ControllerBase
{
private readonly CrearPedidoUseCase _crearPedidoUseCase;
public PedidoController(CrearPedidoUseCase crearPedidoUseCase)
{
_crearPedidoUseCase = crearPedidoUseCase;
}
[HttpPost]
public IActionResult CrearPedido([FromBody] Pedido pedido)
{
_crearPedidoUseCase.Ejecutar(pedido);
return Ok("Pedido creado exitosamente");
}
}
Beneficios de Integrar DDD en Clean Architecture
- Mejor organización del código → Separa correctamente las responsabilidades del negocio y la infraestructura.
- Más expresividad en el dominio → El código representa de manera clara los conceptos de negocio.
- Alta flexibilidad y mantenibilidad → Se pueden cambiar tecnologías sin afectar la lógica del negocio.
- Mejor testabilidad → La lógica de negocio se puede probar sin depender de infraestructura externa.
Errores comunes al integrar DDD en Clean Architecture
- Exponer Entidades directamente en la API → Usar Casos de Uso para interactuar con el dominio.
- Poner lógica de negocio en la Infraestructura o en la Presentación → La lógica debe estar en la Capa de Dominio.
- No usar interfaces en la Capa de Aplicación → La infraestructura debe ser reemplazable sin afectar la aplicación.
Cuestionario de Autoevaluación
- ¿Por qué es importante separar las Entidades y Value Objects en la Capa de Dominio?
- ¿Cuál es el papel de los Agregados en Clean Architecture?
- ¿Cómo se relaciona un Caso de Uso con los Repositorios?
- ¿Por qué la Infraestructura no debe contener lógica de negocio?
- ¿Cómo facilita Clean Architecture la integración con diferentes tecnologías?
Resumen de la Clase
- DDD se integra en Clean Architecture organizando el dominio en Entidades, Value Objects y Agregados.
- Los Casos de Uso en la Capa de Aplicación actúan como intermediarios entre la Presentación y el Dominio.
- Los Repositorios encapsulan la persistencia y permiten desacoplar el acceso a datos.
- Evitar mezclar responsabilidades entre capas garantiza un código más limpio y mantenible.
Próximo paso
En la siguiente clase implementaremos Entidades, Agregados y Value Objects en Clean Architecture con C# para profundizar aún más en el desarrollo real de una aplicación.