0

Arquitectura de Software con C# 13: Ejemplo Práctico – API REST Completa con Clean Architecture y DDD

Introducción a la clase

Hemos visto cómo estructurar una aplicación con Clean Architecture y DDD, implementando Entidades, Agregados, Value Objects, Repositorios, Casos de Uso y Controladores. Ahora vamos a construir una API REST completa en C# con ASP.NET Core y Entity Framework Core.

En esta clase, consolidaremos todo lo aprendido mediante un ejemplo práctico, desarrollando una API de gestión de pedidos.

¿Qué obtendrás de esta clase?

  • Implementarás una API REST funcional con Clean Architecture y DDD.
  • Integrarás EF Core para persistencia de datos.
  • Aprenderás a estructurar correctamente un proyecto en C# con buenas prácticas.
  • Probarás la API con Postman o Swagger.

Paso 1: Estructura del Proyecto

La estructura de carpetas de nuestra API será la siguiente:

/PedidosAPI
│── /Core
│   ├── /Dominio
│   │   ├── Entidades
│   │   ├── ValueObjects
│   │   ├── Agregados
│   ├── /Aplicacion
│   │   ├── CasosDeUso
│   │   ├── Interfaces
│── /Infraestructura
│   ├── Persistencia
│   ├── Repositorios
│── /Presentacion
│   ├── Controladores
│── /Configuracion
│── Program.cs
│── appsettings.json

Paso 2: Implementar el Dominio (Capa de Core – Dominio)

Entidad Pedido

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);
    }
}

Value Object Dirección

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);
    }
}

Paso 3: Implementar la Capa de Aplicación

Interfaz del Repositorio

public interface IPedidoRepository
{
    void Guardar(Pedido pedido);
    Pedido ObtenerPorId(int id);
}

Caso de Uso – Crear Pedido

public class CrearPedidoUseCase
{
    private readonly IPedidoRepository _repository;

    public CrearPedidoUseCase(IPedidoRepository repository)
    {
        _repository = repository;
    }

    public void Ejecutar(Pedido pedido)
    {
        _repository.Guardar(pedido);
    }
}

Paso 4: Implementar la Capa de Infraestructura

DbContext para EF Core

public class AppDbContext : DbContext
{
    public DbSet<Pedido> Pedidos { get; set; }

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Pedido>().HasKey(p => p.Id);
    }
}

Repositorio con EF Core

public class PedidoRepository : IPedidoRepository
{
    private readonly AppDbContext _context;

    public PedidoRepository(AppDbContext context)
    {
        _context = context;
    }

    public void Guardar(Pedido pedido)
    {
        _context.Pedidos.Add(pedido);
        _context.SaveChanges();
    }

    public Pedido ObtenerPorId(int id)
    {
        return _context.Pedidos.FirstOrDefault(p => p.Id == id);
    }
}

Paso 5: Implementar la Capa de Presentación

Controlador REST en ASP.NET Core

[ApiController]
[Route("api/pedidos")]
public class PedidoController : ControllerBase
{
    private readonly CrearPedidoUseCase _crearPedidoUseCase;
    private readonly IPedidoRepository _pedidoRepository;

    public PedidoController(CrearPedidoUseCase crearPedidoUseCase, IPedidoRepository pedidoRepository)
    {
        _crearPedidoUseCase = crearPedidoUseCase;
        _pedidoRepository = pedidoRepository;
    }

    [HttpPost]
    public IActionResult CrearPedido([FromBody] Pedido pedido)
    {
        _crearPedidoUseCase.Ejecutar(pedido);
        return Ok("Pedido creado exitosamente");
    }

    [HttpGet("{id}")]
    public IActionResult ObtenerPedido(int id)
    {
        var pedido = _pedidoRepository.ObtenerPorId(id);
        if (pedido == null)
            return NotFound();

        return Ok(pedido);
    }
}

Paso 6: Configurar la Aplicación en ASP.NET Core

Configurar la Base de Datos y la Inyección de Dependencias

var builder = WebApplication.CreateBuilder(args);

// Configurar Entity Framework Core con SQL Server
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// Configurar inyección de dependencias
builder.Services.AddScoped<IPedidoRepository, PedidoRepository>();
builder.Services.AddScoped<CrearPedidoUseCase>();

var app = builder.Build();

app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

app.Run();

Configurar la conexión a la base de datos en appsettings.json

"ConnectionStrings": {
  "DefaultConnection": "Server=localhost;Database=PedidosDb;Trusted_Connection=True;"
}

Paso 7: Ejecutar la API y Probar con Postman

  1. Ejecuta la API con el siguiente comando:
dotnet run
  1. Crear un Pedido (POST en Postman o cURL)
POST http://localhost:5000/api/pedidos
Content-Type: application/json

{
  "id": 1,
  "cliente": "Juan Pérez"
}
  1. Obtener un Pedido (GET en Postman o cURL)
GET http://localhost:5000/api/pedidos/1

Errores comunes al construir una API REST con Clean Architecture

  • No usar RepositoriosSolución: Separar la lógica de persistencia en la Capa de Infraestructura.
  • No registrar correctamente las dependenciasSolución: Configurar Program.cs con la inyección de dependencias adecuada.
  • No manejar excepciones en la APISolución: Implementar middleware de manejo de errores.

Cuestionario de Autoevaluación

  • ¿Cómo se organiza Clean Architecture en una API REST?
  • ¿Cuál es el propósito de los Casos de Uso en Clean Architecture?
  • ¿Cómo desacoplar la persistencia en Clean Architecture?
  • ¿Cómo probar una API REST sin una base de datos real?
  • ¿Qué beneficios tiene usar una arquitectura modular en aplicaciones grandes?

Resumen de la Clase

  • Hemos construido una API REST completa con Clean Architecture y DDD en C#.
  • Los Casos de Uso permiten mantener la lógica separada de la infraestructura.
  • Usamos EF Core para la persistencia y creamos un Controlador REST en ASP.NET Core.
  • Nuestro código es modular, escalable y fácil de probar.

Próximo paso

En la siguiente clase pasaremos al siguiente clase, donde veremos Arquitectura Hexagonal + DDD en C#.

Fernando Sonego

Deja una respuesta

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