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:
- Presentation (UI) – Kullanıcı arayüzü katmanı (örneğin Razor Pages, WPF, API Controller).
- Business (Service / Application) – İş kurallarını ve mantığı içerir.
- Data Access (Repository / Infrastructure) – Veritabanı veya dış kaynaklarla etkileşimi yönetir.
- Core / Domain – Temel modeller, entity’ler ve arayüzler (isteğe bağlı katman).
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)];
- Domain – Çekirdek: Entity’ler, Value Object’ler, arayüzler.
- Application – Use Case’ler, servisler, arayüzlerin uygulamaya özgü versiyonları.
- Infrastructure – Veritabanı, dosya sistemi, email servisleri vb. dış kaynaklar.
- Presentation – API, web veya masaüstü arayüzü.
Buradaki temel fark: Domain katmanı hiçbir zaman dış katmanlara bağımlı değildir.
Clean Architecture Katmanları ve Görevleri
-
Domain: Uygulamanın çekirdeğidir.
Entity,Value ObjectveInterface’ler burada tanımlanır. İş kuralları burada yaşar. - Application: Domain arayüzlerini kullanarak iş akışlarını (Use Case) yönetir. Örneğin “Sipariş oluştur” veya “Ödemeyi onayla”.
- Infrastructure: Application katmanında tanımlanan arayüzlerin somut uygulamalarıdır. (EF Core, SMTP, dosya yazma, API çağrıları vb.)
- Presentation: Kullanıcıya en yakın katmandır. Web API, MVC veya WPF UI bu katmanda bulunur.
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.
- Domain: Order, Product, Customer entity’leri ve arayüzleri.
- Application: “OrderService” veya “PlaceOrderUseCase”.
- Infrastructure: EF Core tabanlı “EfOrderRepository”.
- Presentation: ASP.NET Core Web API katmanı.
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)
- Katmanlar arası bağımlılığı interface’lerle yönetin.
- Domain katmanı hiçbir NuGet paketine bağımlı olmamalıdır (sadece .NET standard library).
- Use Case bazlı servisler kullanın (örneğin
CreateOrderUseCase). - Entity’lerinizde iş kuralları bulunsun, sadece veri taşıyıcısı olmasın.
- Katmanlar arası DTO / Model dönüşümlerini Mapster veya AutoMapper gibi araçlarla yönetin.
- Her katmanı ayrı proje (assembly) olarak düzenlemek iyi bir pratiktir.
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# Dependency Injection Temelleri
C#’ta Dependency Injection kavramını öğrenin. Bağımlılıkların yönetimi, gevşek bağlılık ve test edilebilirlik örneklerle anlatılıyor.
C# ile Clean Code Prensipleri
C# ile clean code prensiplerini öğrenin. Okunabilir, sürdürülebilir ve bakımı kolay kod yazma teknikleri örneklerle anlatılıyor.
C# ile SOLID Prensipleri
C# örnekleriyle SOLID prensiplerinin uygulanışı: daha esnek, sürdürülebilir ve test edilebilir kod tasarımları.
C# Tasarım Desenleri (Factory, Singleton, Repository, Observer vb.)
C#’ta Factory, Singleton, Repository ve Observer gibi tasarım desenlerini öğrenin. Esnek ve sürdürülebilir yazılım geliştirme örneklerle anlatılıyor.