puedes explicarme cuales son las capas que componen Clean Architecture
Claro. La Clean Architecture organiza el sistema en capas concéntricas con
una regla clave: las dependencias siempre apuntan hacia adentro (las
capas externas dependen de las internas, nunca al revés).
Te lo explico de forma clara y útil 👇
🧠 1. Entidades (Entities)
Son el núcleo del sistema.
Representan las reglas de negocio más puras.
No dependen de nada externo (ni base de datos, ni frameworks).
Ejemplo: Usuario, Pedido, Factura.
👉 Aquí vive la lógica crítica del negocio.
⚙️2. Casos de Uso (Use Cases / Application)
Aquí defines qué hace el sistema.
Orquestan las acciones del negocio.
Usan las entidades para ejecutar procesos.
No conocen detalles técnicos (BD, UI, APIs).
Ejemplo:
Crear usuario
Procesar pago
Generar reporte
👉 Son el “cerebro operativo”.
🔌 3. Interfaces / Adaptadores (Interface Adapters)
Conectan el mundo externo con tu aplicación.
Transforman datos entre formatos (DTOs ↔ entidades).
Incluyen:
o Controladores (Controllers)
o Presentadores
o Repositorios (interfaces)
Ejemplo:
Convertir JSON de una API a objeto Usuario
Mapear datos de BD a entidades
👉 Son traductores.
🌐 4. Frameworks y Drivers (Infraestructura)
Es la capa más externa.
Todo lo técnico:
o Base de datos (SQL Server, MySQL)
o Frameworks (.NET, Spring, etc.)
o APIs externas
o UI (web, móvil)
👉 Aquí está lo reemplazable.
🔄 Regla clave (MUY IMPORTANTE)
Se llama Regla de Dependencia:
➡️Las capas externas dependen de las internas
❌ Las internas NO saben nada de las externas
🧩 Ejemplo sencillo
Imagina crear un usuario:
1. UI (Infraestructura) → recibe datos
2. Controller (Adapters) → los convierte
3. Use Case → ejecuta “Crear Usuario”
4. Entity → valida reglas (ej: edad válida)
🎯 Resumen rápido
Capa Rol
Reglas del
Entidades
negocio
Casos de Lógica de
uso aplicación
Traducción de
Adaptadores
datos
Infraestructu Tecnología
ra externa
Si quieres, puedo ayudarte a armar un proyecto real en .NET paso a
paso con estas capas, incluso usando Dapper como mencionaste antes.
Explicame cómo se estructura un proyecto en .NET con estas capas
Te lo explico de forma práctica, como si fueras a crearlo desde cero en .NET 👇
La idea es traducir la Clean Architecture a una solución real con varios
proyectos.
Estructura típica en .NET
En Visual Studio o CLI, se suele crear una solución así:
[Link]
│
├── [Link]
├── [Link]
├── [Link]
└── [Link] (o UI)
🧠 1. Domain (Entidades)
👉 Núcleo del sistema (NO depende de nadie)
Contenido:
Entidades
Value Objects
Interfaces básicas (repositorios)
Reglas de negocio
Ejemplo:
public class Usuario
{
public int Id { get; set; }
public string Nombre { get; set; }
public void Validar()
{
if ([Link](Nombre))
throw new Exception("El nombre es obligatorio");
}
}
🔑 Importante:
❌ Nada de Entity Framework
❌ Nada de Dapper
❌ Nada de HTTP
⚙️2. Application (Casos de Uso)
👉 Aquí vive la lógica de la aplicación
Contenido:
Casos de uso (Services / Handlers)
DTOs
Interfaces de repositorio (si no están en Domain)
Validaciones
Ejemplo:
public class CrearUsuarioUseCase
{
private readonly IUsuarioRepository _repo;
public CrearUsuarioUseCase(IUsuarioRepository repo)
{
_repo = repo;
}
public async Task Ejecutar(string nombre)
{
var usuario = new Usuario { Nombre = nombre };
[Link]();
await _repo.Crear(usuario);
}
}
🔌 3. Infrastructure (Infraestructura)
👉 Implementa lo técnico
Contenido:
Repositorios reales (Dapper, EF)
Acceso a base de datos
Servicios externos
Unit of Work
Ejemplo con Dapper:
public class UsuarioRepository : IUsuarioRepository
{
private readonly IDbConnection _connection;
public UsuarioRepository(IDbConnection connection)
{
_connection = connection;
}
public async Task Crear(Usuario usuario)
{
var sql = "INSERT INTO Usuarios (Nombre) VALUES (@Nombre)";
await _connection.ExecuteAsync(sql, usuario);
}
}
🌐 4. WebApi (o UI)
👉 Punto de entrada
Contenido:
Controllers
Configuración ([Link])
Inyección de dependencias
Ejemplo:
[ApiController]
[Route("api/[controller]")]
public class UsuarioController : ControllerBase
{
private readonly CrearUsuarioUseCase _useCase;
public UsuarioController(CrearUsuarioUseCase useCase)
{
_useCase = useCase;
}
[HttpPost]
public async Task<IActionResult> Crear(string nombre)
{
await _useCase.Ejecutar(nombre);
return Ok();
}
}
🔗 Dependencias entre proyectos
MUY IMPORTANTE:
WebApi → Infrastructure → Application → Domain
Y también:
Infrastructure → Application
Application → Domain
❌ Domain NO depende de nadie
❌ Application NO depende de Infrastructure
🧩 Registro en [Link]
[Link]<IUsuarioRepository, UsuarioRepository>();
[Link]<CrearUsuarioUseCase>();
🎯 Flujo completo
1. Cliente llama API
2. Controller recibe datos
3. Use Case ejecuta lógica
4. Repository guarda en BD
5. Entity valida reglas
🚀 Tips importantes (esto marca la diferencia)
Usa interfaces para desacoplar
Mantén el Domain limpio
No mezcles lógica de negocio con acceso a datos
Usa DTOs para entrada/salida
Puedes agregar:
o Application/Interfaces
o Application/DTOs
o Infrastructure/Persistence
Si quieres, en el siguiente paso puedo armarte:
✅ Un proyecto completo en .NET paso a paso
✅ Con Dapper + UnitOfWork (como el que ya tienes)
✅ CRUD automático SIN stored procedures
Solo dime 👍
Con Dapper + UnitOfWork (como el que ya tienes) y tambien incluye
IUsuarioRepository ya que lo usaste pero no lo definiste?
Perfecto, vamos a armarte una base real y lista para usar con Dapper +
Unit of Work + IUsuarioRepository, siguiendo la Clean Architecture.
Te dejo todo conectado y coherente 👇
Estructura final
[Link]
│
├── [Link]
├── [Link]
├── [Link]
└── [Link]
🧠 1. DOMAIN
✅ Entidad
public class Usuario
{
public int Id { get; set; }
public string Nombre { get; set; }
public void Validar()
{
if ([Link](Nombre))
throw new Exception("El nombre es obligatorio");
}
}
✅ Interface del repositorio
👉 Aquí defines el contrato
public interface IUsuarioRepository
{
Task Crear(Usuario usuario);
Task<Usuario?> ObtenerPorId(int id);
Task<IEnumerable<Usuario>> ObtenerTodos();
Task Actualizar(Usuario usuario);
Task Eliminar(int id);
}
⚙️2. APPLICATION
✅ Caso de uso
public class UsuarioService
{
private readonly IUsuarioRepository _repo;
public UsuarioService(IUsuarioRepository repo)
{
_repo = repo;
}
public async Task Crear(string nombre)
{
var usuario = new Usuario { Nombre = nombre };
[Link]();
await _repo.Crear(usuario);
}
public Task<IEnumerable<Usuario>> ObtenerTodos()
=> _repo.ObtenerTodos();
}
🔌 3. INFRASTRUCTURE
Aquí metemos Dapper + UnitOfWork + Repository
✅ Unit of Work (clave)
public interface IUnitOfWork : IDisposable
{
IDbTransaction Transaction { get; }
IDbConnection Connection { get; }
Task BeginTransactionAsync();
Task CommitAsync();
Task RollbackAsync();
}
✅ Implementación UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly IDbConnection _connection;
private IDbTransaction _transaction;
public IDbConnection Connection => _connection;
public IDbTransaction Transaction => _transaction;
public UnitOfWork(IDbConnection connection)
{
_connection = connection;
}
public async Task BeginTransactionAsync()
{
if (_connection.State != [Link])
_connection.Open();
_transaction = _connection.BeginTransaction();
await [Link];
}
public async Task CommitAsync()
{
_transaction?.Commit();
await [Link];
}
public async Task RollbackAsync()
{
_transaction?.Rollback();
await [Link];
}
public void Dispose()
{
_transaction?.Dispose();
_connection?.Dispose();
}
}
✅ UsuarioRepository (Dapper)
using Dapper;
public class UsuarioRepository : IUsuarioRepository
{
private readonly IUnitOfWork _uow;
public UsuarioRepository(IUnitOfWork uow)
{
_uow = uow;
}
public async Task Crear(Usuario usuario)
{
var sql = "INSERT INTO Usuarios (Nombre) VALUES (@Nombre)";
await _uow.[Link](sql, usuario, _uow.Transaction);
}
public async Task<Usuario?> ObtenerPorId(int id)
{
var sql = "SELECT * FROM Usuarios WHERE Id = @Id";
return await _uow.[Link]<Usuario>(
sql, new { Id = id }, _uow.Transaction);
}
public async Task<IEnumerable<Usuario>> ObtenerTodos()
{
var sql = "SELECT * FROM Usuarios";
return await _uow.[Link]<Usuario>(
sql, transaction: _uow.Transaction);
}
public async Task Actualizar(Usuario usuario)
{
var sql = "UPDATE Usuarios SET Nombre = @Nombre WHERE Id = @Id";
await _uow.[Link](sql, usuario, _uow.Transaction);
}
public async Task Eliminar(int id)
{
var sql = "DELETE FROM Usuarios WHERE Id = @Id";
await _uow.[Link](sql, new { Id = id },
_uow.Transaction);
}
}
🌐 4. WEB API
✅ Controller
[ApiController]
[Route("api/[controller]")]
public class UsuarioController : ControllerBase
{
private readonly UsuarioService _service;
private readonly IUnitOfWork _uow;
public UsuarioController(UsuarioService service, IUnitOfWork uow)
{
_service = service;
_uow = uow;
}
[HttpPost]
public async Task<IActionResult> Crear(string nombre)
{
await _uow.BeginTransactionAsync();
try
{
await _service.Crear(nombre);
await _uow.CommitAsync();
return Ok();
}
catch (Exception ex)
{
await _uow.RollbackAsync();
return BadRequest([Link]);
}
}
[HttpGet]
public async Task<IActionResult> Obtener()
{
var data = await _service.ObtenerTodos();
return Ok(data);
}
}
🔗 [Link] (DI)
[Link]<IDbConnection>(sp =>
new SqlConnection([Link]("Default")));
[Link]<IUnitOfWork, UnitOfWork>();
[Link]<IUsuarioRepository, UsuarioRepository>();
[Link]<UsuarioService>();
🔄 Flujo real con transacción
1. Controller inicia transacción
2. Service ejecuta lógica
3. Repository usa Dapper
4. UnitOfWork controla commit/rollback
🚀 Mejora PRO (muy recomendada)
Puedes evolucionar esto a:
UnitOfWorkFactory
BaseRepository
Manejo automático de transacciones (middleware)
CQRS (separar lectura/escritura)
Si quieres, en el siguiente paso te puedo armar:
✅ Proyecto completo descargable
✅ CRUD genérico reutilizable
✅ Paginación + filtros
✅ Manejo de errores profesional