0

Arquitectura de Software con C# 09: Cómo Integrar DDD en Clean Architecture

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.

Ubicación de los elementos de DDD en Clean Architecture

Concepto DDDCapa en Clean ArchitecturePropósito
EntidadesDominioRepresentan objetos con identidad única y lógica de negocio
Value ObjectsDominioRepresentan conceptos sin identidad que son inmutables
AgregadosDominioGrupo de Entidades y Value Objects que deben mantenerse consistentes
Casos de UsoAplicaciónOrquestan la ejecución de reglas de negocio en el dominio
RepositoriosInfraestructuraImplementan 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.

Fernando Sonego

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *