# 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. - Published: 2026-08-24 - Updated: 2026-08-24 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- 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. > **Mülakat İpucu** > > 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](https://learn.microsoft.com/en-us/dotnet/fundamentals/) 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. ```csharp // OrderService.cs public class OrderService { private readonly IOrderRepository _orderRepository; private readonly INotificationService _notificationService; public OrderService( IOrderRepository orderRepository, INotificationService notificationService) { _orderRepository = orderRepository; _notificationService = notificationService; } public async Task 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. ```csharp // IPaymentProcessor.cs public interface IPaymentProcessor { string PaymentMethod { get; } Task ProcessAsync(Payment payment); } // StripePaymentProcessor.cs public class StripePaymentProcessor : IPaymentProcessor { public string PaymentMethod => "Stripe"; public async Task 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 _processors; public PaymentService(IEnumerable processors) { _processors = processors; } public async Task 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](https://github.com/jasontaylordev/CleanArchitecture) 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 | ```csharp // Domain/Entities/Customer.cs 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. > **Neden Private Setter'lar?** > > 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. ```csharp // Program.cs (Minimal API) var builder = WebApplication.CreateBuilder(args); // Domain services - typically transient builder.Services.AddTransient(); // Application services - scoped per request builder.Services.AddScoped(); // Infrastructure - scoped to share DbContext builder.Services.AddScoped(); builder.Services.AddScoped(); // External clients - singleton with HttpClient pooling builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri("https://api.stripe.com/v1/"); }); // Keyed services for multiple implementations (.NET 8+) builder.Services.AddKeyedScoped("email"); builder.Services.AddKeyedScoped("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](/blog/dotnet/ef-core-performance-best-practices) bakılabilir. ```csharp // Application/Interfaces/IOrderRepository.cs public interface IOrderRepository { Task GetByIdAsync(Guid id, CancellationToken ct = default); Task> 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 GetByIdAsync(Guid id, CancellationToken ct = default) { return await _context.Orders .Include(o => o.Items) .FirstOrDefaultAsync(o => o.Id == id, ct); } public async Task> 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. ## 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](https://xunit.net/) ve [NSubstitute](https://nsubstitute.github.io/), .NET için test framework'ü ve mocking kütüphanesi sağlar. ```csharp // OrderServiceTests.cs public class OrderServiceTests { private readonly IOrderRepository _orderRepository; private readonly INotificationService _notificationService; private readonly OrderService _sut; public OrderServiceTests() { _orderRepository = Substitute.For(); _notificationService = Substitute.For(); _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()); await _notificationService.Received(1) .SendOrderConfirmationAsync(Arg.Is(o => o.Id == order.Id)); } [Fact] public async Task CreateOrderAsync_EmptyItems_ThrowsDomainException() { // Arrange var request = new CreateOrderRequest( CustomerId: Guid.NewGuid(), Items: Array.Empty()); // Act & Assert await Assert.ThrowsAsync( () => _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](/technologies/dotnet/interview-questions/clean-architecture) bakılabilir. > **Yaygın Mülakat Tuzağı** > > 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. ```csharp // CachingOrderRepository.cs (Decorator pattern) 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 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(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. ```csharp // Application/Orders/Queries/OrderDto.cs public record OrderDto( Guid Id, Guid CustomerId, IReadOnlyList 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. ```csharp // 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 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. ## 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 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/dotnet/clean-code-architecture-csharp-guide-interview-questions-2026