Arquitectura en capas y Clean Architecture en C#
Aprende arquitectura en capas y Clean Architecture en C# para crear aplicaciones mantenibles y bien estructuradas.
A medida que los proyectos de software crecen, mantener el código organizado y sostenible se vuelve cada vez más importante. Para este propósito, se utilizan la arquitectura en capas (Layered Architecture) y su evolución moderna, la arquitectura limpia (Clean Architecture). Ambos modelos proporcionan una base sólida para gestionar dependencias, separar responsabilidades y construir sistemas comprobables y extensibles.
¿Qué es la arquitectura en capas?
La arquitectura en capas es un modelo clásico que divide una aplicación en capas con responsabilidades diferentes. Su forma más común consta de 3 o 4 capas:
- Presentación (UI) – Capa de interfaz de usuario (por ejemplo, Razor Pages, WPF, API Controllers).
- Negocio (Servicio / Aplicación) – Contiene las reglas y la lógica de negocio.
- Acceso a Datos (Repositorio / Infraestructura) – Gestiona la comunicación con la base de datos o recursos externos.
- Núcleo / Dominio – Define modelos, entidades e interfaces base (capa opcional).
La dirección de las dependencias es de arriba hacia abajo: la UI depende del negocio, el negocio depende del acceso a datos, pero no al revés.
Flujo clásico de la arquitectura en capas
El siguiente diagrama muestra un ejemplo simple:
graph TD;
A[Capa UI] --> B[Capa de Negocio];
B --> C[Capa de Acceso a Datos];
C --> D[Base de Datos];
Esta estructura es suficiente para sistemas CRUD simples, pero puede volverse compleja cuando las capas están muy acopladas.
¿Qué es la Clean Architecture?
La Clean Architecture, propuesta por Robert C. Martin (Uncle Bob), orienta las dependencias hacia el centro (el dominio). Su objetivo es mantener las reglas de negocio completamente aisladas del mundo exterior.
En esta arquitectura, las dependencias van ahora de afuera hacia adentro:
graph LR;
A[UI / API] --> B[Aplicación];
B --> C[Dominio];
C --> D[Infraestructura (Externa)];
- Dominio – El núcleo: entidades, objetos de valor e interfaces.
- Aplicación – Casos de uso, servicios y lógica de aplicación.
- Infraestructura – Implementa dependencias externas (bases de datos, archivos, correo, APIs, etc.).
- Presentación – Capa de interfaz: API web, aplicación web o de escritorio.
La diferencia clave: la capa de Dominio nunca depende de las capas externas.
Capas y responsabilidades en la Clean Architecture
-
Dominio: El núcleo de la aplicación.
Contiene las
Entidades,Objetos de ValoryInterfaces. Aquí residen las reglas de negocio. - Aplicación: Usa las interfaces del Dominio para orquestar los flujos de negocio (casos de uso). Ejemplo: “Crear Pedido”, “Aprobar Pago”.
- Infraestructura: Implementa las interfaces definidas en la capa de Aplicación. (por ejemplo, EF Core, SMTP, almacenamiento de archivos, llamadas a APIs)
- Presentación: La capa más cercana al usuario – API Web, MVC o WPF.
Regla de Dependencia
El principio fundamental de la Clean Architecture es que las dependencias siempre apuntan hacia el centro. Las capas externas pueden conocer las internas, pero las internas no deben conocer las externas.
// Capa Dominio (sin dependencias)
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public bool IsApproved { get; private set; }
public void Approve()
{
if (Total <= 0)
throw new InvalidOperationException("El total debe ser mayor que 0.");
IsApproved = true;
}
}
// Capa Aplicación
public interface IOrderRepository
{
Task<Order> GetByIdAsync(int id);
Task SaveAsync(Order order);
}
public class ApproveOrderUseCase
{
private readonly IOrderRepository _repository;
public ApproveOrderUseCase(IOrderRepository repository)
=> _repository = repository;
public async Task ExecuteAsync(int id)
{
var order = await _repository.GetByIdAsync(id);
order.Approve();
await _repository.SaveAsync(order);
}
}
// Capa Infraestructura
public class EfOrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public EfOrderRepository(AppDbContext context) => _context = context;
public Task<Order> GetByIdAsync(int id) =>
_context.Orders.FirstOrDefaultAsync(o => o.Id == id);
public Task SaveAsync(Order order)
{
_context.Update(order);
return _context.SaveChangesAsync();
}
}
// Capa Presentación (API)
[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
private readonly ApproveOrderUseCase _useCase;
public OrdersController(ApproveOrderUseCase useCase) => _useCase = useCase;
[HttpPost("{id}/approve")]
public async Task Approve(int id)
{
await _useCase.ExecuteAsync(id);
return Ok();
}
}
Como se puede ver, la capa de Dominio no conoce nada del código de infraestructura, y la capa de Aplicación solo trabaja con interfaces. Esto permite cambiar dependencias fácilmente (por ejemplo, sustituir EF Core por Dapper).
Gestión de dependencias entre capas
Las dependencias suelen resolverse mediante inyección de dependencias (DI) en la capa de Infraestructura o en el Composition Root:
// Program.cs o Startup.cs
builder.Services.AddScoped<IOrderRepository, EfOrderRepository>();
builder.Services.AddScoped<ApproveOrderUseCase>();
Así, el controlador solo conoce ApproveOrderUseCase sin importar qué repositorio se utilice.
Diferencias entre la Arquitectura en Capas y la Clean Architecture
| Característica | Arquitectura en Capas Clásica | Clean Architecture |
|---|---|---|
| Dirección de dependencias | De arriba hacia abajo | De afuera hacia adentro (hacia el núcleo) |
| Dependencia de la base de datos | La lógica de negocio depende de la base de datos | El dominio es independiente del acceso a datos |
| Testabilidad | Difícil | Fácil (interfaces simuladas) |
| Modificabilidad | Capas fuertemente acopladas | Dependencias gestionadas por abstracciones |
| Mantenimiento a largo plazo | Difícil de mantener | Modular y fácil de mantener |
Ejemplo real: aplicación de comercio electrónico
A continuación se muestra un ejemplo simplificado de una aplicación de Clean Architecture en un sistema de comercio electrónico:
- Dominio: Entidades e interfaces: Pedido, Producto, Cliente.
- Aplicación: Casos de uso como “OrderService” o “PlaceOrderUseCase”.
- Infraestructura: Repositorio basado en EF Core “EfOrderRepository”.
- Presentación: Capa Web API de ASP.NET Core.
Con esta estructura, incluso si la interfaz de usuario cambia (por ejemplo, API → WPF), las reglas de negocio (Dominio y Aplicación) permanecen intactas.
Buenas prácticas
- Gestiona las dependencias entre capas mediante interfaces.
- La capa de Dominio no debe depender de ningún paquete externo (solo .NET Standard).
- Utiliza servicios basados en casos de uso (por ejemplo,
CreateOrderUseCase). - Las entidades deben contener la lógica de negocio, no solo datos.
- Usa herramientas como Mapster o AutoMapper para las conversiones entre DTOs y modelos.
- Organizar cada capa como un proyecto (ensamblado) independiente es una buena práctica.
Estructura de proyecto de ejemplo
MyApp/
├── MyApp.Domain/
│ ├── Entities/
│ ├── Interfaces/
│ └── Exceptions/
├── MyApp.Application/
│ ├── UseCases/
│ ├── Services/
│ └── DTOs/
├── MyApp.Infrastructure/
│ ├── Persistence/
│ └── Repositories/
└── MyApp.API/
├── Controllers/
└── Program.cs
Esta estructura representa una solución modular en .NET conforme a los principios de Clean Architecture.
TL;DR
- Arquitectura en capas: Modelo clásico con flujo UI → Negocio → Datos.
- Clean Architecture: Invierte las dependencias; el núcleo es el Dominio.
- Dominio es independiente, Aplicación gestiona el flujo, Infraestructura maneja los sistemas externos.
- Inyección de dependencias es la clave para gestionar dependencias.
- Cada capa debe tener una responsabilidad clara, ser comprobable y reemplazable.
- Clean Architecture es ideal para sistemas sostenibles, mantenibles y escalables.
Artículos relacionados
Fundamentos de Inyección de Dependencias en C#
Aprende los fundamentos de Inyección de Dependencias en C#, gestionando dependencias y logrando bajo acoplamiento.
Patrones de diseño en C# (Factory, Singleton, Repository, Observer)
Aprende patrones de diseño en C#, como Factory, Singleton y Repository, para desarrollar aplicaciones escalables y mantenibles.
Principios de Clean Code en C#
Aprende principios de Clean Code en C# para escribir código legible, mantenible y escalable con ejemplos prácticos.
Principios SOLID en C#
Aplicación de los principios SOLID en C# con ejemplos: código más flexible, mantenible y comprobable.