Sección transversal de un tronco de árbol con anillos concéntricos, iluminada en púrpura sobre fondo negro

Clean Architecture en ASP.NET Core: lo que aprendí liderando una migración real

Hay muchos artículos sobre Clean Architecture. La mayoría explican la teoría: capas, dependencias invertidas, casos de uso. Pocos cuentan lo que pasa cuando la aplicas a un proyecto real con presión de entrega, equipo de tamaño medio y un legacy que no se puede tirar de golpe.

Este es uno de esos pocos artículos.

El punto de partida

Lideré la migración de una solución de escritorio legacy hacia .NET 8 + .NET MAUI. No era un proyecto de laboratorio: cada error tenía coste real.

La decisión de adoptar Clean Architecture no fue ideológica. Fue pragmática: necesitábamos que el código fuera testeable, que los cambios en la capa de datos no rompieran la lógica de negocio, y que los desarrolladores junior del equipo pudieran trabajar en paralelo sin pisarse. Clean Architecture resolvía los tres problemas.

Cómo aplicamos Clean Architecture en ASP.NET Core

La estructura que adoptamos en ASP.NET Core (aplicable también a MAUI) fue la siguiente:

  • Domain: entidades, interfaces de repositorio, value objects. Sin dependencias externas. Cero referencias a EF Core, cero referencias a frameworks de UI.
  • Application: casos de uso (Commands y Queries via CQRS/MediatR), DTOs, interfaces de servicios externos. Depende solo de Domain.
  • Infrastructure: implementaciones concretas — repositorios EF Core, clientes HTTP, servicios de mensajería (RabbitMQ). Depende de Application y Domain.
  • Presentation: controllers de ASP.NET Core (o ViewModels en MAUI). Orquesta los casos de uso via MediatR. Depende de Application.

La regla fundamental: las dependencias siempre apuntan hacia adentro. Domain no sabe que existe EF Core. Application no sabe que existe SQL Server.

Lo que funcionó

CQRS con MediatR. Separar Commands (escritura) de Queries (lectura) resuelve el 80% de los problemas de complejidad en capas de aplicación. Cada caso de uso es un handler independiente: fácil de testear, fácil de revisar en code review.

Interfaces en Application, implementaciones en Infrastructure. Cuando tuvimos que cambiar el proveedor de mensajería, el cambio fue quirúrgico: un fichero de Infrastructure, cero cambios en Application. Eso es lo que promete la arquitectura limpia — y en este caso cumplió.

Logging estructurado desde el principio. NLog configurado en la capa de Infrastructure con output a fichero y consola. Los logs tenían contexto (usuario, operación, identificador de dispositivo). Cuando algo fallaba en producción, el diagnóstico era cuestión de minutos, no de horas.

Lo que aprendí de lo que no funcionó

No todo necesita ser un caso de uso. Al principio creamos un Command y un Handler para operaciones que eran lecturas simples de configuración. Overhead puro. La regla que adoptamos: si la operación no tiene lógica de negocio y no necesita transacción, puede vivir directamente en el repositorio y llamarse desde el controller sin pasar por MediatR.

Los DTOs de Application y los ViewModels de Presentation son cosas distintas. Nos costó separar el DTO que usa Application (lo que necesita el caso de uso) del ViewModel que usa la UI (lo que necesita renderizar). Son capas diferentes con responsabilidades diferentes. Mezclarlos genera acoplamiento que luego cuesta caro.

¿Vale la pena para proyectos pequeños?

Depende de hacia dónde va el proyecto. Si es un script de integración que se ejecuta una vez al día y nunca va a crecer: no. Si es un servicio que va a recibir features durante años y lo va a tocar más de una persona: sí.

La Clean Architecture tiene un coste de arranque real — más ficheros, más proyectos, más interfaces. Ese coste se amortiza en cuanto el proyecto crece o cambia. En proyectos que nunca crecen, es ruido.

La clave no es aplicar la arquitectura a ciegas porque «es lo correcto». La clave es entender qué problema resuelve y decidir si ese problema lo tienes.

Si trabajas en .NET y tienes dudas sobre cómo estructurar tu solución, escríbeme a contacto@guerrerocode.net. Es lo que hago en desarrollo .NET a medida.