Clean Code Architecture C#: Guía Completa y Preguntas de Entrevista 2026
Domina los principios de Clean Code y Clean Architecture en C# para crear aplicaciones .NET mantenibles y testeables. Prepara tus entrevistas técnicas con ejemplos prácticos.

Clean Code Architecture en C# combina los principios de Clean Code de Robert C. Martin con el patrón Clean Architecture para producir aplicaciones .NET mantenibles, testeables y escalables. Las entrevistas técnicas se centran cada vez más en estos conceptos porque revelan cómo un candidato piensa sobre el diseño de software más allá de simplemente hacer que el código funcione.
Cuando los entrevistadores preguntan sobre Clean Architecture, esperan que los candidatos expliquen la regla de dependencia: las dependencias del código fuente apuntan hacia adentro, hacia las políticas de nivel superior. La capa Domain no conoce nada sobre Infrastructure, no al revés.
Principios SOLID como Base del Clean Code en C#
Los principios SOLID forman la columna vertebral del Clean Code en C#. La documentación oficial de Microsoft sobre fundamentos de .NET recomienda estos patrones para aplicaciones empresariales. Cada principio aborda un problema de mantenimiento específico.
Principio de Responsabilidad Única (SRP): Una clase tiene una sola razón para cambiar. El OrderService a continuación maneja únicamente el procesamiento de pedidos, delegando la persistencia y las notificaciones a componentes separados.
public class OrderService
{
private readonly IOrderRepository _orderRepository;
private readonly INotificationService _notificationService;
public OrderService(
IOrderRepository orderRepository,
INotificationService notificationService)
{
_orderRepository = orderRepository;
_notificationService = notificationService;
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
// Validates and creates the domain entity
var order = Order.Create(request.CustomerId, request.Items);
// Persists through the repository abstraction
await _orderRepository.AddAsync(order);
// Notifies through a separate service
await _notificationService.SendOrderConfirmationAsync(order);
return order;
}
}Principio Abierto/Cerrado (OCP): Las clases permanecen abiertas para extensión pero cerradas para modificación. Los nuevos métodos de pago requieren nuevas clases, no cambios a las existentes.
public interface IPaymentProcessor
{
string PaymentMethod { get; }
Task<PaymentResult> ProcessAsync(Payment payment);
}
// StripePaymentProcessor.cs
public class StripePaymentProcessor : IPaymentProcessor
{
public string PaymentMethod => "Stripe";
public async Task<PaymentResult> ProcessAsync(Payment payment)
{
// Stripe-specific implementation
var charge = await _stripeClient.CreateChargeAsync(payment.Amount);
return new PaymentResult(charge.Id, charge.Status == "succeeded");
}
}
// PaymentService.cs
public class PaymentService
{
private readonly IEnumerable<IPaymentProcessor> _processors;
public PaymentService(IEnumerable<IPaymentProcessor> processors)
{
_processors = processors;
}
public async Task<PaymentResult> ProcessPaymentAsync(
Payment payment, string method)
{
var processor = _processors
.FirstOrDefault(p => p.PaymentMethod == method)
?? throw new NotSupportedException($"Payment method {method} not supported");
return await processor.ProcessAsync(payment);
}
}Agregar soporte para PayPal significa agregar una clase PayPalPaymentProcessor. El PaymentService permanece sin cambios.
Las Cuatro Capas de Clean Architecture en .NET
Clean Architecture organiza el código en capas concéntricas. El repositorio Clean Architecture de Jason Taylor proporciona una plantilla .NET ampliamente adoptada. Cada capa tiene responsabilidades explícitas y las dependencias fluyen hacia adentro.
| Capa | Responsabilidad | Dependencias |
|---|---|---|
| Domain | Entidades, objetos de valor, eventos de dominio | Ninguna |
| Application | Casos de uso, DTOs, interfaces | Domain |
| Infrastructure | Base de datos, APIs externas, sistema de archivos | Application, Domain |
| Presentation | Controladores, vistas, endpoints de API | Application |
public class Customer
{
public Guid Id { get; private set; }
public string Email { get; private set; }
public CustomerStatus Status { get; private set; }
private Customer() { } // EF Core constructor
public static Customer Create(string email)
{
if (string.IsNullOrWhiteSpace(email))
throw new DomainException("Email cannot be empty");
return new Customer
{
Id = Guid.NewGuid(),
Email = email.ToLowerInvariant(),
Status = CustomerStatus.Active
};
}
public void Deactivate()
{
if (Status == CustomerStatus.Inactive)
throw new DomainException("Customer already inactive");
Status = CustomerStatus.Inactive;
}
}La entidad Domain encapsula las reglas de negocio. Valida sus propios invariantes y expone comportamiento a través de métodos, no setters.
Los setters privados evitan que el código externo ponga las entidades en estados inválidos. El método Deactivate() aplica la regla de que los clientes inactivos no pueden ser desactivados nuevamente. Este patrón aparece frecuentemente en implementaciones de Domain-Driven Design y Clean Architecture.
Patrones de Inyección de Dependencias para Clean Architecture
La Inyección de Dependencias (DI) habilita la inversión de dependencias requerida por Clean Architecture. .NET 10 incluye un contenedor DI incorporado que soporta inyección por constructor, tiempos de vida con alcance y servicios con clave introducidos en .NET 8.
var builder = WebApplication.CreateBuilder(args);
// Domain services - typically transient
builder.Services.AddTransient<IOrderValidator, OrderValidator>();
// Application services - scoped per request
builder.Services.AddScoped<IOrderService, OrderService>();
// Infrastructure - scoped to share DbContext
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
// External clients - singleton with HttpClient pooling
builder.Services.AddHttpClient<IPaymentGateway, StripeGateway>(client =>
{
client.BaseAddress = new Uri("https://api.stripe.com/v1/");
});
// Keyed services for multiple implementations (.NET 8+)
builder.Services.AddKeyedScoped<INotificationService, EmailNotificationService>("email");
builder.Services.AddKeyedScoped<INotificationService, SmsNotificationService>("sms");
var app = builder.Build();La capa Application define las interfaces. La capa Infrastructure las implementa. La capa Presentation (o raíz de composición) conecta todo.
Patrón Repository con Entity Framework Core 9
El patrón Repository abstrae el acceso a datos detrás de interfaces centradas en el dominio. EF Core 9, incluido con .NET 10, proporciona el ORM subyacente.
public interface IOrderRepository
{
Task<Order?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<IReadOnlyList<Order>> GetByCustomerAsync(Guid customerId, CancellationToken ct = default);
Task AddAsync(Order order, CancellationToken ct = default);
void Update(Order order);
}
// Infrastructure/Repositories/OrderRepository.cs
public class OrderRepository : IOrderRepository
{
private readonly ApplicationDbContext _context;
public OrderRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<Order?> GetByIdAsync(Guid id, CancellationToken ct = default)
{
return await _context.Orders
.Include(o => o.Items)
.FirstOrDefaultAsync(o => o.Id == id, ct);
}
public async Task<IReadOnlyList<Order>> GetByCustomerAsync(
Guid customerId, CancellationToken ct = default)
{
return await _context.Orders
.Where(o => o.CustomerId == customerId)
.OrderByDescending(o => o.CreatedAt)
.ToListAsync(ct);
}
public async Task AddAsync(Order order, CancellationToken ct = default)
{
await _context.Orders.AddAsync(order, ct);
}
public void Update(Order order)
{
_context.Orders.Update(order);
}
}El repositorio retorna entidades de dominio, no DTOs. El mapeo a DTOs ocurre en la capa Application, manteniendo el Domain puro.
¿Listo para aprobar tus entrevistas de .NET?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Pruebas Unitarias de Componentes Clean Architecture
Clean Architecture facilita las pruebas porque las dependencias se inyectan a través de interfaces. xUnit y NSubstitute proporcionan el framework de pruebas y la biblioteca de mocks para .NET.
public class OrderServiceTests
{
private readonly IOrderRepository _orderRepository;
private readonly INotificationService _notificationService;
private readonly OrderService _sut;
public OrderServiceTests()
{
_orderRepository = Substitute.For<IOrderRepository>();
_notificationService = Substitute.For<INotificationService>();
_sut = new OrderService(_orderRepository, _notificationService);
}
[Fact]
public async Task CreateOrderAsync_ValidRequest_PersistsAndNotifies()
{
// Arrange
var request = new CreateOrderRequest(
CustomerId: Guid.NewGuid(),
Items: new[] { new OrderItemDto("SKU-001", 2, 29.99m) });
// Act
var order = await _sut.CreateOrderAsync(request);
// Assert
await _orderRepository.Received(1).AddAsync(Arg.Any<Order>());
await _notificationService.Received(1)
.SendOrderConfirmationAsync(Arg.Is<Order>(o => o.Id == order.Id));
}
[Fact]
public async Task CreateOrderAsync_EmptyItems_ThrowsDomainException()
{
// Arrange
var request = new CreateOrderRequest(
CustomerId: Guid.NewGuid(),
Items: Array.Empty<OrderItemDto>());
// Act & Assert
await Assert.ThrowsAsync<DomainException>(
() => _sut.CreateOrderAsync(request));
}
}Las pruebas verifican comportamiento, no implementación. El _sut (system under test) interactúa con mocks. Las aserciones verifican que los métodos correctos fueron llamados con los argumentos correctos.
Preguntas de Entrevista sobre Clean Architecture C#
Las entrevistas técnicas evalúan la comprensión de Clean Architecture en múltiples niveles. Estas preguntas aparecen en entrevistas .NET de nivel senior.
Los candidatos frecuentemente confunden Clean Architecture con arquitectura N-tier. La diferencia clave: en Clean Architecture, las dependencias apuntan hacia adentro, hacia el Domain. En N-tier, cada capa depende de la que está debajo, haciendo que el Domain dependa de Infrastructure.
P: ¿Cómo difiere Clean Architecture de la arquitectura en capas tradicional?
La arquitectura en capas tradicional tiene cada capa dependiendo de la que está debajo: Presentation depende de Business Logic, que depende de Data Access. Clean Architecture invierte esto: el Domain no tiene dependencias, Application depende de Domain, e Infrastructure depende de ambos. Esta inversión significa que la tecnología de base de datos puede cambiar sin tocar las reglas de negocio.
P: ¿Cuándo elegirías no usar Clean Architecture?
Clean Architecture agrega indirección. Para aplicaciones pesadamente CRUD sin reglas de negocio complejas, la sobrecarga excede el beneficio. Una API simple que hace proxy directo a tablas de base de datos no necesita cuatro capas. El valor emerge cuando la complejidad de la lógica de negocio justifica la separación.
P: ¿Cómo manejas las preocupaciones transversales como logging y caching?
Dos patrones funcionan bien: el patrón decorator y middleware. Un decorator de caché envuelve la interfaz del repositorio, implementando la misma interfaz mientras agrega lógica de caché. El logging típicamente usa middleware o un interceptor de DI que envuelve las llamadas de servicio sin contaminar la lógica de negocio.
public class CachingOrderRepository : IOrderRepository
{
private readonly IOrderRepository _inner;
private readonly IDistributedCache _cache;
public CachingOrderRepository(
IOrderRepository inner,
IDistributedCache cache)
{
_inner = inner;
_cache = cache;
}
public async Task<Order?> GetByIdAsync(Guid id, CancellationToken ct = default)
{
var cacheKey = $"order:{id}";
var cached = await _cache.GetStringAsync(cacheKey, ct);
if (cached is not null)
return JsonSerializer.Deserialize<Order>(cached);
var order = await _inner.GetByIdAsync(id, ct);
if (order is not null)
{
await _cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(order),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
},
ct);
}
return order;
}
// Other methods delegate to _inner
}P: ¿Cómo estructuras la validación en Clean Architecture?
La validación ocurre en dos niveles. La validación de dominio (invariantes) pertenece a las entidades: un pedido no puede tener cero artículos. La validación de aplicación (validación de entrada) pertenece a los handlers de comandos o validadores: la solicitud debe incluir un ID de cliente válido. FluentValidation se integra bien para validación de entrada, mientras que la validación de dominio permanece en constructores y métodos de entidades.
Organización del Código y Convenciones de Nomenclatura
La estructura del proyecto comunica la arquitectura. La plantilla estándar de Clean Architecture organiza proyectos por capa.
src/
├── MyApp.Domain/
│ ├── Entities/
│ ├── ValueObjects/
│ ├── Events/
│ └── Exceptions/
├── MyApp.Application/
│ ├── Common/
│ │ ├── Interfaces/
│ │ └── Behaviors/
│ ├── Orders/
│ │ ├── Commands/
│ │ ├── Queries/
│ │ └── EventHandlers/
│ └── Customers/
├── MyApp.Infrastructure/
│ ├── Persistence/
│ ├── Services/
│ └── Configuration/
└── MyApp.WebApi/
├── Controllers/
├── Filters/
└── Middleware/Los cortes verticales (Orders, Customers) agrupan casos de uso relacionados. Esta organización escala mejor que los cortes horizontales (Commands, Queries) a medida que la aplicación crece.
Consideraciones de Rendimiento en Clean Architecture
La abstracción tiene un costo. Cada llamada de interfaz agrega indirección. Estas prácticas minimizan la sobrecarga mientras preservan la testeabilidad.
Usar records para DTOs: Los records generan implementaciones eficientes de Equals y GetHashCode. Son inmutables por defecto, previniendo mutaciones accidentales.
public record OrderDto(
Guid Id,
Guid CustomerId,
IReadOnlyList<OrderItemDto> Items,
decimal Total,
DateTime CreatedAt);
public record OrderItemDto(
string Sku,
int Quantity,
decimal UnitPrice);Evitar la sobre-abstracción: No toda clase necesita una interfaz. Abstraer dependencias externas (base de datos, clientes HTTP, sistema de archivos). Los servicios de dominio internos que tienen una sola implementación raramente necesitan interfaces.
Perfilar antes de optimizar: La sobrecarga de las capas de Clean Architecture es típicamente insignificante comparada con operaciones de I/O. Una consulta de base de datos que toma 50ms eclipsa los microsegundos gastados en dispatch de métodos.
Aplicando Principios Clean Code a Métodos C#
Clean Code se enfoca en la legibilidad a nivel de métodos y clases. Estas prácticas aplican independientemente del patrón arquitectónico.
Los métodos hacen una sola cosa: Un método llamado ProcessOrderAndSendEmail viola SRP. Dividirlo en ProcessOrder y SendOrderConfirmation.
Nombres significativos: CalculateOrderTotal comunica intención. DoCalculation no. Los nombres de variables siguen la misma regla: customerOrders sobre list.
Métodos pequeños: Si un método excede 20 líneas, probablemente hace demasiado. Extraer métodos auxiliares con nombres descriptivos.
// Before: Long method doing multiple things
public decimal CalculateInvoiceTotal(Invoice invoice)
{
decimal subtotal = 0;
foreach (var item in invoice.Items)
{
subtotal += item.Quantity * item.UnitPrice;
}
decimal discount = 0;
if (invoice.Customer.IsPreferred)
{
discount = subtotal * 0.1m;
}
decimal tax = (subtotal - discount) * 0.2m;
return subtotal - discount + tax;
}
// After: Small methods with single responsibilities
public decimal CalculateInvoiceTotal(Invoice invoice)
{
var subtotal = CalculateSubtotal(invoice.Items);
var discount = CalculateDiscount(subtotal, invoice.Customer);
var tax = CalculateTax(subtotal - discount);
return subtotal - discount + tax;
}
private decimal CalculateSubtotal(IEnumerable<InvoiceItem> items)
=> items.Sum(item => item.Quantity * item.UnitPrice);
private decimal CalculateDiscount(decimal subtotal, Customer customer)
=> customer.IsPreferred ? subtotal * 0.1m : 0;
private decimal CalculateTax(decimal taxableAmount)
=> taxableAmount * 0.2m;La versión refactorizada se lee como un resumen de la lógica de negocio. Cada método auxiliar puede probarse independientemente.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Puntos Clave para Clean Code Architecture en C#
- La regla de dependencia: las dependencias del código apuntan hacia adentro, de Infrastructure hacia Domain, nunca hacia afuera
- Los principios SOLID guían el diseño de clases: responsabilidad única, abierto para extensión, sustitución de Liskov, segregación de interfaces, inversión de dependencias
- Cuatro capas separan las preocupaciones: Domain (entidades), Application (casos de uso), Infrastructure (sistemas externos), Presentation (API/UI)
- La inyección de dependencias conecta las capas en la raíz de composición, típicamente en
Program.cs - El patrón Repository abstrae el acceso a datos detrás de interfaces centradas en el dominio que retornan entidades, no DTOs
- Las pruebas unitarias verifican comportamiento usando mocks de interfaces, validando que los métodos correctos reciben los argumentos correctos
- Clean Code a nivel de métodos: métodos pequeños, nombres significativos, responsabilidades únicas
- El costo de rendimiento de la abstracción es típicamente insignificante comparado con operaciones de I/O; perfilar antes de optimizar
- Las preguntas de entrevista evalúan la comprensión de la regla de dependencia, compromisos y patrones prácticos como decorators para preocupaciones transversales
¿Sabrías detectar el bug en .NET?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 24 de agosto de 2026
Etiquetas
Compartir
Artículos relacionados

LINQ Avanzado en C# en 2026: Operadores, Rendimiento y Preguntas de Entrevista
Dominar los operadores LINQ avanzados, optimizar el rendimiento de consultas y prepararse para entrevistas técnicas C# con ejemplos prácticos.

.NET 10 en 2026: Nuevas Funcionalidades, AOT Nativo y Preguntas de Entrevista
Descubre las nuevas características de .NET 10 en 2026: compilación AOT nativa para producción, C# 14 con extension members y field keyword, ASP.NET Core 10 y preguntas técnicas de entrevista.

Clean Architecture con .NET: Guía Práctica
Domina Clean Architecture en .NET con C#. Aprende los principios SOLID, la separación de capas y los patrones de implementación para aplicaciones mantenibles.