Clean Code Architecture C#: Kapsamlı Rehber ve Mülakat Soruları 2026
C# dilinde Clean Code ve Clean Architecture kavramlarını SOLID prensipleri, bağımlılık enjeksiyonu ve .NET 10 mülakat soruları ile öğrenin.

C# dilinde Clean Code Architecture, Robert C. Martin'in Clean Code prensiplerini Clean Architecture deseni ile birleştirerek bakımı kolay, test edilebilir ve ölçeklenebilir .NET uygulamaları oluşturur. Teknik mülakatlar bu kavramlara giderek daha fazla odaklanmaktadır çünkü bir adayın yazılım tasarımı hakkındaki düşünce yapısını sadece kodun çalışmasının ötesinde ortaya koyarlar.
Clean Architecture hakkında sorulduğunda, mülakatçılar adayların bağımlılık kuralını açıklamasını bekler: kaynak kod bağımlılıkları içe doğru, daha yüksek seviyeli politikalara doğru işaret eder. Domain katmanı Infrastructure hakkında hiçbir şey bilmez, tam tersi değil.
SOLID Prensipleri: Temiz C# Kodunun Temeli
SOLID prensipleri, C# dilinde Clean Code'un omurgasını oluşturur. Microsoft'un resmi .NET temel dokümantasyonu kurumsal uygulamalar için bu desenleri önerir. Her prensip belirli bir bakım sorununu ele alır.
Tek Sorumluluk Prensibi (SRP): Bir sınıfın değişmesi için tek bir neden olmalıdır. Aşağıdaki OrderService yalnızca sipariş işleme ile ilgilenir, kalıcılık ve bildirimleri ayrı bileşenlere devreder.
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;
}
}Açık/Kapalı Prensibi (OCP): Sınıflar genişletmeye açık ancak değişikliğe kapalı kalmalıdır. Yeni ödeme yöntemleri mevcut sınıflarda değişiklik değil, yeni sınıflar gerektirir.
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);
}
}PayPal desteği eklemek PayPalPaymentProcessor sınıfı eklemek anlamına gelir. PaymentService değişmeden kalır.
.NET'te Clean Architecture'ın Dört Katmanı
Clean Architecture, kodu eşmerkezli katmanlar halinde düzenler. Jason Taylor'ın Clean Architecture deposu yaygın olarak benimsenen bir .NET şablonu sağlar. Her katmanın açık sorumlulukları vardır ve bağımlılıklar içe doğru akar.
| Katman | Sorumluluk | Bağımlılıklar |
|---|---|---|
| Domain | Varlıklar, değer nesneleri, domain olayları | Yok |
| Application | Kullanım senaryoları, DTO'lar, arayüzler | Domain |
| Infrastructure | Veritabanı, harici API'ler, dosya sistemi | Application, Domain |
| Presentation | Controller'lar, görünümler, API endpoint'leri | 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;
}
}Domain varlığı iş kurallarını kapsüller. Kendi değişmezlerini doğrular ve davranışı setter'lar yerine metotlar aracılığıyla sunar.
Private setter'lar, dış kodun varlıkları geçersiz durumlara sokmasını engeller. Deactivate() metodu, pasif müşterilerin tekrar pasifleştirilemeyeceği kuralını uygular. Bu desen, Domain-Driven Design ve Clean Architecture implementasyonlarında sıkça görülür.
Clean Architecture için Bağımlılık Enjeksiyonu Desenleri
Bağımlılık Enjeksiyonu (DI), Clean Architecture'ın gerektirdiği bağımlılık tersine çevirmeyi sağlar. .NET 10, constructor enjeksiyonu, scoped lifetime'lar ve .NET 8'de tanıtılan keyed service'leri destekleyen yerleşik bir DI konteyneri içerir.
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();Application katmanı arayüzleri tanımlar. Infrastructure katmanı bunları uygular. Presentation katmanı (veya composition root) her şeyi bir araya getirir.
Entity Framework Core 9 ile Repository Deseni
Repository deseni, veri erişimini domain odaklı arayüzlerin arkasına soyutlar. .NET 10 ile gelen EF Core 9, temel ORM'i sağlar. Gelişmiş EF Core desenleri için SharpSkill EF Core performans rehberine bakılabilir.
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);
}
}Repository, DTO'lar değil domain varlıkları döndürür. DTO'lara eşleme Application katmanında gerçekleşir ve Domain'in saflığı korunur.
.NET mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Clean Architecture Bileşenlerinin Birim Testi
Clean Architecture, bağımlılıklar arayüzler aracılığıyla enjekte edildiğinden test etmeyi kolaylaştırır. xUnit ve NSubstitute, .NET için test framework'ü ve mocking kütüphanesi sağlar.
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));
}
}Testler, implementasyonu değil davranışı doğrular. _sut (test edilen sistem) mock'larla etkileşime girer. Assertion'lar doğru metotların doğru argümanlarla çağrılıp çağrılmadığını kontrol eder.
Clean Architecture C# Mülakat Soruları
Teknik mülakatlar, Clean Architecture anlayışını birden fazla düzeyde sorgular. Bu sorular kıdemli .NET mülakatlarında karşılaşılır. Daha fazla hazırlık materyali için SharpSkill Clean Architecture mülakat modülüne bakılabilir.
Adaylar sıklıkla Clean Architecture'ı N-tier mimari ile karıştırır. Temel fark: Clean Architecture'da bağımlılıklar içe doğru, Domain'e doğru işaret eder. N-tier'da her katman altındaki katmana bağımlıdır, bu da Domain'i Infrastructure'a bağımlı kılar.
S: Clean Architecture geleneksel katmanlı mimariden nasıl farklıdır?
Geleneksel katmanlı mimari, her katmanı altındaki katmana bağımlı kılar: Presentation, Business Logic'e bağımlıdır; Business Logic da Data Access'e bağımlıdır. Clean Architecture bunu tersine çevirir: Domain'in hiç bağımlılığı yoktur, Application Domain'e bağımlıdır ve Infrastructure her ikisine de bağımlıdır. Bu tersine çevirme, veritabanı teknolojisinin iş kurallarına dokunmadan değişebileceği anlamına gelir.
S: Clean Architecture'ı ne zaman kullanmazsınız?
Clean Architecture dolaylama ekler. Karmaşık iş kuralları olmayan CRUD ağırlıklı uygulamalar için yük, faydayı aşar. Veritabanı tablolarını doğrudan proxy'leyen basit bir API'nin dört katmana ihtiyacı yoktur. Değer, iş mantığı karmaşıklığı ayrımı haklı kıldığında ortaya çıkar.
S: Loglama ve önbellekleme gibi kesişen ilgileri nasıl ele alırsınız?
İki desen iyi çalışır: dekoratör deseni ve middleware. Önbellek dekoratörü, repository arayüzünü sarar, aynı arayüzü uygulayarak önbellek mantığı ekler. Loglama tipik olarak iş mantığını kirletmeden servis çağrılarını saran middleware veya DI interceptor kullanır.
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
}S: Clean Architecture'da doğrulamayı nasıl yapılandırırsınız?
Doğrulama iki seviyede gerçekleşir. Domain doğrulaması (değişmezler) varlıklara aittir: Bir Sipariş sıfır öğeye sahip olamaz. Uygulama doğrulaması (girdi doğrulaması) komut işleyicilere veya doğrulayıcılara aittir: istek geçerli bir müşteri ID'si içermelidir. FluentValidation girdi doğrulaması için iyi entegre olurken, domain doğrulaması varlık constructor'larında ve metotlarında kalır.
Kod Organizasyonu ve Adlandırma Kuralları
Proje yapısı mimariyi iletir. Standart Clean Architecture şablonu, projeleri katmanlara göre düzenler.
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/Dikey dilimler (Orders, Customers) ilgili kullanım senaryolarını gruplar. Bu organizasyon, uygulama büyüdükçe yatay dilimlerden (Commands, Queries) daha iyi ölçeklenir.
Clean Architecture'da Performans Değerlendirmeleri
Soyutlamanın bir maliyeti vardır. Her arayüz çağrısı dolaylama ekler. Bu pratikler, test edilebilirliği korurken yükü en aza indirir.
DTO'lar için record kullanın: Record'lar verimli Equals ve GetHashCode implementasyonları oluşturur. Varsayılan olarak değişmezdirler ve kazara mutasyonu önlerler.
public record OrderDto(
Guid Id,
Guid CustomerId,
IReadOnlyList<OrderItemDto> Items,
decimal Total,
DateTime CreatedAt);
public record OrderItemDto(
string Sku,
int Quantity,
decimal UnitPrice);Aşırı soyutlamadan kaçının: Her sınıfın bir arayüze ihtiyacı yoktur. Dış bağımlılıkları (veritabanı, HTTP istemcileri, dosya sistemi) soyutlayın. Tek implementasyonlu dahili domain servisleri nadiren arayüzlere ihtiyaç duyar.
Optimize etmeden önce profil çıkarın: Clean Architecture katmanlarının yükü, I/O işlemlerine kıyasla genellikle ihmal edilebilir düzeydedir. 50ms süren bir veritabanı sorgusu, metot dispatch'te harcanan mikrosaniyeleri gölgede bırakır.
Clean Code Prensiplerini C# Metotlarına Uygulama
Clean Code, metot ve sınıf düzeyinde okunabilirliğe odaklanır. Bu pratikler mimari desenden bağımsız olarak geçerlidir.
Metotlar tek bir şey yapar: ProcessOrderAndSendEmail adlı bir metot SRP'yi ihlal eder. ProcessOrder ve SendOrderConfirmation olarak ayırın.
Anlamlı isimler: CalculateOrderTotal amacı iletir. DoCalculation iletmez. Değişken isimleri de aynı kurala tabidir: list yerine customerOrders.
Küçük metotlar: Bir metot 20 satırı aşıyorsa, muhtemelen çok fazla şey yapıyordur. Açıklayıcı isimlerle yardımcı metotlar çıkarın.
// 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;Yeniden düzenlenen versiyon, iş mantığının bir özeti gibi okunur. Her yardımcı metot bağımsız olarak test edilebilir.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
C# Clean Code Architecture için Temel Çıkarımlar
- Bağımlılık kuralı: kod bağımlılıkları içe doğru, Infrastructure'dan Domain'e doğru işaret eder, asla dışa doğru değil
- SOLID prensipleri sınıf tasarımına rehberlik eder: tek sorumluluk, genişletmeye açıklık, Liskov yerine koyma, arayüz ayrımı, bağımlılık tersine çevirme
- Dört katman sorumlulukları ayırır: Domain (varlıklar), Application (kullanım senaryoları), Infrastructure (dış sistemler), Presentation (API/UI)
- Bağımlılık enjeksiyonu, katmanları composition root'ta birbirine bağlar, genellikle
Program.cs'de - Repository deseni, veri erişimini DTO'lar değil varlıklar döndüren domain odaklı arayüzlerin arkasına soyutlar
- Birim testleri, arayüzleri mock'layarak davranışı doğrular, doğru metotların doğru argümanları aldığını valide eder
- Metot düzeyinde Clean Code: küçük metotlar, anlamlı isimler, tek sorumluluklar
- Soyutlamanın performans maliyeti, I/O işlemlerine kıyasla genellikle ihmal edilebilir düzeydedir; optimize etmeden önce profil çıkarın
- Mülakat soruları, bağımlılık kuralı, trade-off'lar ve kesişen ilgiler için dekoratörler gibi pratik desenlerin anlaşılmasını sorgular
.NET kodundaki hatayı bulabilir misin?
Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Yazan:
Anthony Fillion-MailletSharpSkill kurucusu
10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.
24 Ağustos 2026 tarihinde güncellendi
Paylaş
İlgili makaleler

2026'da İleri Düzey C# LINQ: Operatörler, Performans ve Mülakat Soruları
İleri düzey LINQ operatörlerini, ertelenmiş yürütmeyi ve performans optimizasyonunu öğrenin. GroupBy, SelectMany, sorgu optimizasyonu ve .NET geliştiricileri için yaygın mülakat sorularını kapsar.

ASP.NET Core 2026'da SignalR: Gerçek Zamanlı İletişim, Hub'lar ve Mülakat Soruları
ASP.NET Core'da SignalR için kapsamlı rehber. Gerçek zamanlı iletişim, hub yapılandırması, grup yönetimi ve mülakat hazırlığı konularını öğrenin.

2026'da ASP.NET Core Minimal API'ler: Mimari, Performans ve Mülakat Soruları
ASP.NET Core Minimal API'lere kapsamlı rehber - mimari, route grupları, endpoint filtreleri, Native AOT ve .NET geliştiricileri için kritik mülakat soruları.