Yükleniyor...

C# Katmanlı Mimari ve Clean Architecture

C#’ta katmanlı mimari ve Clean Architecture yaklaşımını öğrenin. Bağımlılık yönetimi ve sürdürülebilir proje yapıları anlatılıyor.

Yazılım projeleri büyüdükçe, kodun düzenli ve sürdürülebilir olması daha da önem kazanır. Bu amaçla katmanlı mimari (Layered Architecture) ve onun modern evrimi olan Clean Architecture yaklaşımı kullanılır. Her iki model de bağımlılıkları yönetmek, sorumlulukları ayırmak ve test edilebilir, genişletilebilir sistemler inşa etmek için güçlü temel sağlar.


Katmanlı Mimari Nedir?

Katmanlı mimari, uygulamayı farklı sorumluluklara sahip katmanlara ayıran klasik bir yazılım mimarisidir. En yaygın formu 3 veya 4 katmandan oluşur:

Katmanlar bağımlılık yönü olarak yukarıdan aşağıya bağlıdır. Yani UI, Business’a; Business, Data Access’e erişebilir ama tersi olmamalıdır.


Klasik Katmanlı Mimari Akışı

Aşağıdaki şemada basit bir örnek gösterilmektedir:


graph TD;
  A[UI Katmanı] --> B[Business Katmanı];
  B --> C[Data Access Katmanı];
  C --> D[Database];

Bu yapı basit CRUD tabanlı sistemler için yeterlidir; ancak katmanlar arasında sıkı bağımlılıklar oluştuğunda zamanla karmaşıklaşır.


Clean Architecture Nedir?

Clean Architecture, Robert C. Martin (Uncle Bob) tarafından geliştirilen, bağımlılıkları merkeze (domain’e) yönlendiren bir mimari yaklaşımdır. Amacı, uygulamanın iş kurallarını (business rules) dış dünyadan tamamen izole tutmaktır.

Katmanlar artık dıştan içe değil, içten dışa bağımlıdır:


graph LR;
  A[UI / API] --> B[Application];
  B --> C[Domain];
  C --> D[Infrastructure (External)];

Buradaki temel fark: Domain katmanı hiçbir zaman dış katmanlara bağımlı değildir.


Clean Architecture Katmanları ve Görevleri


Bağımlılık Kuralları (Dependency Rule)

Clean Architecture’ın en temel ilkesi: Bağımlılıklar her zaman merkeze doğru ilerler. Yani, dış katmanlar iç katmanlara bağımlıdır; ancak iç katmanlar dış katmanları bilmez.


// Domain Katmanı (Hiçbir şeye bağımlı değil)
public class Order
{
    public int Id { get; set; }
    public decimal Total { get; set; }
    public bool IsApproved { get; private set; }

    public void Approve()
    {
        if (Total <= 0)
            throw new InvalidOperationException("Tutar 0'dan büyük olmalı.");
        IsApproved = true;
    }
}

// Application Katmanı
public interface IOrderRepository
{
    Task<Order> GetByIdAsync(int id);
    Task SaveAsync(Order order);
}

public class ApproveOrderUseCase
{
    private readonly IOrderRepository _repository;
    public ApproveOrderUseCase(IOrderRepository repository)
        => _repository = repository;

    public async Task ExecuteAsync(int id)
    {
        var order = await _repository.GetByIdAsync(id);
        order.Approve();
        await _repository.SaveAsync(order);
    }
}

// Infrastructure Katmanı
public class EfOrderRepository : IOrderRepository
{
    private readonly AppDbContext _context;
    public EfOrderRepository(AppDbContext context) => _context = context;

    public Task<Order> GetByIdAsync(int id) =>
        _context.Orders.FirstOrDefaultAsync(o => o.Id == id);

    public Task SaveAsync(Order order)
    {
        _context.Update(order);
        return _context.SaveChangesAsync();
    }
}

// Presentation Katmanı (API)
[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
    private readonly ApproveOrderUseCase _useCase;
    public OrdersController(ApproveOrderUseCase useCase) => _useCase = useCase;

    [HttpPost("{id}/approve")]
    public async Task Approve(int id)
    {
        await _useCase.ExecuteAsync(id);
        return Ok();
    }
}

Gördüğünüz gibi Domain katmanı hiçbir altyapı kodunu bilmez; Application katmanı ise sadece arayüzlerle çalışır. Bu sayede bağımlılıklar kolayca değiştirilebilir (örneğin EF Core yerine Dapper kullanılabilir).


Katmanlar Arasında Bağımlılık Yönetimi

Bağımlılıklar genellikle Dependency Injection (DI) aracılığıyla Infrastructure veya Composition Root katmanında çözülür:


// Program.cs veya Startup.cs
builder.Services.AddScoped<IOrderRepository, EfOrderRepository>();
builder.Services.AddScoped<ApproveOrderUseCase>();

Böylece Controller sadece ApproveOrderUseCase’i bilir; hangi repository’nin kullanıldığını bilmez.


Clean Architecture ve Katmanlı Mimari Arasındaki Farklar

Özellik Klasik Katmanlı Mimari Clean Architecture
Bağımlılık Yönü Yukarıdan aşağıya Dıştan içe (merkeze)
Veritabanı Bağımlılığı İş mantığı veritabanına bağımlıdır Domain, veri erişiminden bağımsızdır
Test Edilebilirlik Zor Kolay (mock interface’ler)
Değiştirilebilirlik Katmanlar birbirine sıkı bağlı Bağımlılıklar soyutlamalarla yönetilir
Uzun Ömürlü Projeler Bakımı zorlaşır Bakımı kolay, modüler yapı

Gerçek Hayat Örneği: E-Ticaret Uygulaması

Aşağıda basitleştirilmiş bir e-ticaret sisteminde Clean Architecture katmanları örneklenmiştir.

Bu yapı sayesinde UI tamamen değişse (örneğin API yerine WPF yazılsa) bile iş kuralları (Domain & Application) etkilenmez.


En İyi Uygulamalar (Best Practices)


Proje Dizini Örneği


MyApp/
├── MyApp.Domain/
│   ├── Entities/
│   ├── Interfaces/
│   └── Exceptions/
├── MyApp.Application/
│   ├── UseCases/
│   ├── Services/
│   └── DTOs/
├── MyApp.Infrastructure/
│   ├── Persistence/
│   └── Repositories/
└── MyApp.API/
    ├── Controllers/
    └── Program.cs

Bu yapı, Clean Architecture yaklaşımına uygun, bağımsız katmanlı bir .NET çözüm yapısıdır.


TL;DR

  • Katmanlı Mimari: UI → Business → Data yönlü klasik modeldir.
  • Clean Architecture: Bağımlılıkları tersine çevirir; merkez Domain’dir.
  • Domain bağımsız, Application akış yönetir, Infrastructure dış sistemlerle ilgilenir.
  • Dependency Injection bağımlılık yönetiminin kalbidir.
  • Her katman ayrı sorumluluk almalı, test edilebilir ve değiştirilebilir olmalıdır.
  • Clean Architecture uzun ömürlü, sürdürülebilir sistemler için idealdir.

İlişkili Makaleler

C# ile SOLID Prensipleri

C# örnekleriyle SOLID prensiplerinin uygulanışı: daha esnek, sürdürülebilir ve test edilebilir kod tasarımları.