Clean Architecture (Temiz Mimari) Nedir? Katmanlar, Prensipler ve Gerçek Bir Projede Uygulama
Clean Architecture ile iş kurallarını framework, veritabanı ve dış servislerden ayırarak daha sürdürülebilir, test edilebilir ve değişime dayanıklı yazılımlar tasarlamayı öğrenin.

Bir projeye başlarken kodun düzenli olması kolaydır.
Birkaç controller, birkaç service, birkaç entity derken proje büyür. Sonra bir gün basit görünen bir değişiklik gelir: Veritabanı değişecek, ödeme servisi yenilenecek, API yerine farklı bir arayüz eklenecek veya mevcut iş kuralı değişecek.
İşte sorun genellikle burada başlar.
Birbirine bağımlı hale gelmiş controller'lar, service'ler, repository'ler ve veritabanı kodları değişikliği pahalı hale getirir. Bir satırı değiştirmek için beş farklı dosyayı kontrol etmek gerekir. Test yazmak zorlaşır. Yeni geliştirici projeye girdiğinde kodun nerede ne yaptığını anlaması zaman alır.
Clean Architecture'ın temel amacı tam olarak bu problemi çözmektir: İş kurallarını uygulamanın dış dünyasından ayırmak.
Bu yaklaşım yalnızca klasörleri Domain, Application, Infrastructure şeklinde bölmek değildir. Asıl mesele, sistemdeki bağımlılıkların hangi yönde aktığını kontrol etmektir.

Clean Architecture nedir?
Clean Architecture, yazılımın temel iş kurallarını veritabanı, framework, UI ve dış servisler gibi değişmesi muhtemel detaylardan bağımsız tutmayı amaçlayan bir mimari yaklaşımdır.
Yaklaşım Robert C. Martin tarafından 2012 yılında "The Clean Architecture" makalesiyle popüler hale geldi ve daha sonra aynı isimli kitapta kapsamlı biçimde ele alındı.
Temel fikir oldukça basit:
İş kurallarınız PostgreSQL'e, SQL Server'a, Entity Framework'e, React'e veya belirli bir API framework'üne bağımlı olmak zorunda değildir.
Örneğin bir e-ticaret uygulamasında:
Sipariş oluşturma
İndirim hesaplama
Stok kontrolü
Ödeme kuralları
Kullanıcı yetkileri
gibi kurallar uygulamanın merkezindedir.
Bunların yanında:
SQL Server
PostgreSQL
Entity Framework Core
ASP.NET Core
Redis
Stripe
RabbitMQ
REST API
gibi teknolojiler bulunabilir.
Clean Architecture, merkeze iş kurallarını koyup teknolojik detayları bunun çevresinde konumlandırmayı önerir.
Clean Architecture neden kullanılır?
Clean Architecture'ın asıl değeri klasör yapısında değil, değişiklik maliyetini azaltmasında ortaya çıkar.
Bir uygulamada veritabanını değiştirdiğinizde iş kurallarının yeniden yazılması gerekmemelidir.
Aynı şekilde API yerine farklı bir arayüz eklediğinizde domain mantığının değişmesi beklenmez.
İyi tasarlanmış bir sistemde şu değişiklikler mümkün olduğunca birbirinden bağımsız yapılabilir:
| | |
| Değişiklik | İş kuralları etkilenmeli mi? |
| SQL Server → PostgreSQL | Hayır |
| REST API → başka UI | Hayır |
| EF Core → başka ORM | Hayır |
| Gerçek ödeme servisi → test servisi | Hayır |
| Email sağlayıcısı değişikliği | Hayır |
| İş kuralının kendisi değişirse | Evet |
Buradaki önemli nokta şu:
Clean Architecture değişiklikleri ortadan kaldırmaz. Değişikliğin sistem içinde yayılmasını sınırlar.
Clean Architecture katmanları
Clean Architecture'ın farklı projelerde farklı isimlerle uygulanması mümkün olsa da .NET dünyasında sık karşılaşılan yapı yaklaşık olarak şöyledir:

Burada kritik olan yalnızca katmanların varlığı değildir.
Bağımlılıkların yönüdür.
Dış katmanlar iç katmanlara bağımlı olabilir. İç katmanlar dış katmanları bilmemelidir.
Bu fikir Clean Architecture'ın en önemli prensiplerinden biridir:
Dependency Rule: Bağımlılıklar içeriye doğru ilerler
Clean Architecture'ın merkezindeki düşünce Dependency Rule olarak ifade edilir.
Basitleştirirsek:
Infrastructure ──────► Application ──────► Domain
Presentation ───────► Application ──────► Domain
Ancak:
Domain ──────X──────► Infrastructure
Domain ──────X──────► Database
Domain ──────X──────► ASP.NET Core
olmamalıdır.
Örneğin Order entity'sinin Entity Framework Core'u bilmesine gerek yoktur.
Kötü bir örnek:
public class Order
{
public DbContext Context { get; set; }
public async Task SaveAsync()
{
await Context.SaveChangesAsync();
}
}
Burada domain modeli veritabanı teknolojisine bağımlı hale gelir.
Daha temiz yaklaşımda domain yalnızca iş kurallarını bilir:
public class Order
{
public decimal Total { get; private set; }
public void ApplyDiscount(decimal percentage)
{
Total -= Total * percentage / 100;
}
}
Veritabanına kaydetme işlemi ise dış katmanda gerçekleştirilir.
Domain Layer nedir?
Domain Layer uygulamanın iş kurallarının merkezidir.
Burada teknoloji bağımlılığını mümkün olduğunca düşük tutmak istersiniz.
Domain içerisinde genellikle:
Entity'ler
Value Object'ler
Domain kuralları
Domain event'ler
İş mantığı
bulunabilir.
Örneğin bir banka uygulamasında:
Account
Money
Transaction
Transfer
gibi kavramlar domain'in parçası olabilir.
Bir hesabın bakiyesinden fazla para çekilememesi gibi bir kural da domain seviyesinde bulunabilir.
public class BankAccount
{
public decimal Balance { get; private set; }
public void Withdraw(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Tutar pozitif olmalıdır.");
if (amount > Balance)
throw new InvalidOperationException("Yetersiz bakiye.");
Balance -= amount;
}
}
Burada dikkat edilmesi gereken şey BankAccount sınıfının:
SQL Server'ı
HTTP'yi
Controller'ı
Entity Framework'ü
bilmemesidir.
Bu bağımsızlık domain'i test etmeyi de oldukça kolaylaştırır.
Application Layer nedir?
Domain iş kurallarının merkezidir. Application Layer ise bu kuralların hangi kullanım senaryolarında nasıl çalıştırılacağını organize eder.
Başka bir ifadeyle Application Layer genellikle use case'leri temsil eder.
Örneğin:
CreateOrder
CancelOrder
GetOrder
UpdateCustomer
RegisterUser
ChangePassword
gibi işlemler Application katmanında bulunabilir.
Basit bir örnek:
public class CreateOrderHandler
{
private readonly IOrderRepository _orderRepository;
public CreateOrderHandler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task Handle(CreateOrderCommand command)
{
var order = new Order(command.CustomerId);
// İş akışı burada yönetilir.
await _orderRepository.AddAsync(order);
}
}
Burada önemli bir ayrıntı var:
CreateOrderHandler, siparişin nereye kaydedildiğini bilmek zorunda değildir.
Sadece IOrderRepository üzerinden ihtiyaç duyduğu davranışı ister.
Infrastructure Layer nedir?
Infrastructure, uygulamanın dış dünya ile konuştuğu katmandır.
Örneğin:
Entity Framework Core
SQL Server
PostgreSQL
Redis
SMTP
Dosya sistemi
Harici API'ler
Cloud servisleri
Message broker'lar
burada bulunabilir.
Application içerisinde:
public interface IOrderRepository
{
Task AddAsync(Order order);
Task<Order?> GetByIdAsync(Guid id);
}
şeklinde bir abstraction tanımlanabilir.
Infrastructure bunu implemente eder:
public class OrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public OrderRepository(AppDbContext context)
{
_context = context;
}
public async Task AddAsync(Order order)
{
await _context.Orders.AddAsync(order);
await _context.SaveChangesAsync();
}
public async Task<Order?> GetByIdAsync(Guid id)
{
return await _context.Orders.FindAsync(id);
}
}
Böylece Application Layer:
"SQL Server'a git ve bu tabloya INSERT at."
demez.
Şunu söyler:
"Bir siparişi kaydet."
Bu küçük fark, büyük sistemlerde ciddi bir tasarım avantajı sağlar.
Presentation Layer ne yapar?
Presentation Layer kullanıcının veya başka sistemlerin uygulamayla iletişim kurduğu noktadır.
.NET Web API örneğinde burası genellikle:
Controller
Endpoint
Request modeli
Response modeli
Authentication/authorization entegrasyonu
gibi bileşenleri içerir.
Örneğin:
[HttpPost]
public async Task<IActionResult> Create(CreateOrderRequest request)
{
var command = new CreateOrderCommand(
request.CustomerId
);
await _handler.Handle(command);
return Ok();
}
Controller'ın temel görevi burada mümkün olduğunca basit kalır.
İş kurallarını controller'a doldurmak yerine ilgili application use case'ine devreder.
Clean Architecture klasör yapısı nasıl olmalı?

.NET projelerinde örnek bir yapı şöyle olabilir:
src/
│
├── MyApp.Domain/
│ ├── Entities/
│ ├── ValueObjects/
│ ├── Events/
│ └── Exceptions/
│
├── MyApp.Application/
│ ├── Features/
│ │ ├── Orders/
│ │ │ ├── Commands/
│ │ │ └── Queries/
│ │ ├── Users/
│ │ └── Products/
│ ├── Interfaces/
│ ├── DTOs/
│ └── Behaviors/
│
├── MyApp.Infrastructure/
│ ├── Persistence/
│ ├── Repositories/
│ ├── Services/
│ └── ExternalServices/
│
└── MyApp.API/
├── Controllers/
├── Middleware/
├── Filters/
└── Program.cs
Bu yapı bir standart değildir.
Clean Architecture'ı uygulamak için klasör isimlerini birebir kullanmanız gerekmez.
Önemli olan şu soruya cevap verebilmektir:
"Bu kod neden burada?"
Clean Architecture ile klasik katmanlı mimari arasındaki fark
İki yaklaşım dışarıdan bakıldığında benzer görünebilir.
Klasik bir uygulamada:
Controller
↓
Service
↓
Repository
↓
Database
yapısı bulunabilir.
Bu tek başına kötü değildir.
Sorun, zaman içinde Service katmanının bütün iş mantığını taşıyan devasa bir sınıfa dönüşmesiyle başlar.
Clean Architecture ise iş kurallarının nerede yaşadığını daha net tanımlar ve bağımlılıkların yönünü kontrol eder.
Örneğin:
API
↓
Application
↓
Domain
↑
Infrastructure
Infrastructure dışarıda olmasına rağmen Application'ın tanımladığı abstraction'ları implement edebilir.
Bu nedenle mesele yalnızca "kaç katmanımız var?" değildir.
Mesele, sistemin hangi parçalarının diğerlerine bağımlı olduğudur.
Clean Architecture ile Onion Architecture aynı şey mi?
Tam olarak aynı kavram değillerdir ancak güçlü şekilde ilişkilidir.
Onion Architecture, Jeffrey Palermo tarafından ortaya konan ve domain modelini merkeze alan bir mimari yaklaşımdır.
Clean Architecture da benzer şekilde iş kurallarını merkeze koyar ve bağımlılıkların merkeze doğru ilerlemesini savunur.
Her iki yaklaşımın ortak fikri şudur:
Domain, altyapı detaylarından bağımsız olmalıdır.
Bu yüzden pratikte projelerde Clean Architecture ve Onion Architecture prensiplerinin birlikte kullanıldığını görmek oldukça yaygındır.
Clean Architecture ne zaman kullanılmalı?
Her proje için Clean Architecture kurmak zorunda değilsiniz.
Küçük bir CRUD uygulamasında dört ayrı proje oluşturmak gereksiz bir karmaşıklık yaratabilir.
Örneğin yalnızca:
Products
Categories
Customers
içeren küçük ve kısa ömürlü bir yönetim paneli için daha basit bir yapı yeterli olabilir.
Clean Architecture daha çok şu durumlarda anlam kazanır:
Proje uzun süre geliştirilecekse
Birden fazla geliştirici çalışıyorsa
Karmaşık iş kuralları varsa
Test edilebilirlik önemliyse
Dış servis sayısı fazlaysa
Veritabanı veya altyapı değişebilir durumdaysa
Uygulamanın büyümesi bekleniyorsa
Domain mantığı CRUD'dan daha karmaşıksa
Burada önemli bir ayrım var:
Clean Architecture küçük projeleri otomatik olarak daha iyi yapmaz.
Bazen mimarinin kendisi, çözmeye çalıştığınız problemden daha karmaşık hale gelebilir.
Clean Architecture'ın avantajları
Test edilebilirlik
Domain kodu veritabanına veya HTTP'ye bağlı olmadığında unit test yazmak kolaylaşır.
Örneğin:
[Fact]
public void Withdraw_Should_Fail_When_Balance_Is_Insufficient()
{
var account = new BankAccount(100);
Assert.Throws<InvalidOperationException>(
() => account.Withdraw(150)
);
}
Test için veritabanına bağlanmanız gerekmez.
Bu, testleri hızlı ve güvenilir hale getirir.
Değiştirilebilirlik
Veritabanını değiştirmek istediğinizde domain kurallarını yeniden yazmanız gerekmemelidir.
Aynı şekilde Stripe yerine başka bir ödeme sağlayıcısına geçmek, mümkün olduğunca Infrastructure değişikliği olarak kalmalıdır.
Daha anlaşılır iş mantığı
İş kuralları controller, repository ve database kodlarının arasına dağılmadığında sistemin davranışını anlamak kolaylaşır.
Takım çalışması
Sorumlulukların ayrılması, farklı geliştiricilerin farklı alanlarda daha bağımsız çalışmasını kolaylaştırabilir.
Clean Architecture'ın dezavantajları
Bu yaklaşımın maliyeti de vardır.
Daha fazla kod
Basit bir işlem için:
Controller
Command
Handler
Interface
Repository
Entity
DTO
Mapping
gibi birden fazla yapı oluşturabilirsiniz.
Bu, küçük projelerde gereksiz olabilir.
Öğrenme eğrisi
Dependency Inversion, abstraction, dependency injection, domain modelleme ve use case yaklaşımı yeni başlayan geliştiriciler için ilk etapta karmaşık gelebilir.
Aşırı soyutlama
Her sınıf için interface oluşturmak Clean Architecture değildir.
Örneğin:
IProductService
IProductServiceFactory
IProductServiceProvider
gibi gereksiz abstraction'lar kodu daha temiz değil, daha karmaşık hale getirebilir.
Soyutlama bir amaç değil, değişiklik sınırlarını kontrol etmek için kullanılan bir araçtır.
Clean Architecture'da en sık yapılan hatalar
1. Sadece klasörleri ayırmak
Şöyle bir yapı oluşturmak tek başına Clean Architecture değildir:
Domain/
Application/
Infrastructure/
API/
Eğer Application doğrudan EF Core'a, Domain SQL'e veya Controller business logic'e bağımlıysa klasör isimlerinin pek anlamı kalmaz.
2. Domain'i anemik hale getirmek
Entity'leri sadece property taşıyan modeller olarak kullanmak ve bütün davranışı service sınıflarına taşımak bazı domain'lerde iş kurallarının dağılmasına neden olabilir.
Örneğin:
order.Total = order.Total - discount;
gibi her yerden değiştirilebilen state yerine:
order.ApplyDiscount(discount);
gibi davranışın domain içerisinde korunması daha anlamlı olabilir.
3. Her şeyi interface yapmak
Interface sayısı arttıkça mimarinin kalitesi otomatik olarak artmaz.
Bir abstraction'ın gerçekten bir sorunu çözmesi gerekir.
4. Application katmanını ikinci bir Infrastructure yapmak
Application içerisinde:
new SqlConnection(...)
veya doğrudan HTTP client kullanmaya başladığınızda sınırlar bozulmaya başlar.
Application ne yapılacağını bilmeli; dış sistemle bunun nasıl yapılacağını Infrastructure çözmelidir.
5. Framework'ü merkeze koymak
Projenin tamamını ASP.NET Core özelliklerine bağlamak, framework değişikliğini zorlaştırabilir.
Framework uygulamanın kendisi değildir.
Framework, uygulamanın çalışmasını sağlayan detaylardan biridir.
Clean Architecture + CQRS birlikte kullanılmalı mı?
Bu ikisi sık sık beraber kullanılır ancak birbirlerinin zorunlu parçası değildir.
Clean Architecture bir mimari yaklaşımdır.
CQRS ise okuma ve yazma işlemlerinin ayrıştırılmasına yönelik bir tasarım yaklaşımıdır.
Örneğin Application katmanında:
Commands/
CreateOrderCommand
CancelOrderCommand
Queries/
GetOrderQuery
GetCustomerOrdersQuery
şeklinde bir yapı kurulabilir.
Ancak basit bir uygulamada CQRS eklemek yerine daha sade bir use case yapısı tercih edilebilir.
Yani:
Clean Architecture kullanıyorsanız CQRS kullanmak zorunda değilsiniz.
Gerçek bir projede nasıl uygulanır?
Örneğin bir blog platformunda kullanıcı yazı yayınlıyor olsun.
Bir yazı yayınlamak için şu kurallarımız olduğunu düşünelim:
Kullanıcı aktif olmalı.
Başlık boş olmamalı.
İçerik minimum uzunluğu karşılamalı.
Kullanıcının yayınlama yetkisi olmalı.
Yazı oluşturulmalı.
Veritabanına kaydedilmeli.
Arama indeksine gönderilmeli.
Bunu tek bir controller içine koymak yerine use case olarak modelleyebiliriz.
PublishArticle
│
├── Validate user
├── Validate article
├── Create Article
├── Save Article
└── Publish event
Domain:
public class Article
{
public string Title { get; private set; }
public string Content { get; private set; }
public Article(string title, string content)
{
if (string.IsNullOrWhiteSpace(title))
throw new ArgumentException("Başlık zorunludur.");
if (string.IsNullOrWhiteSpace(content))
throw new ArgumentException("İçerik zorunludur.");
Title = title;
Content = content;
}
}
Application:
public class PublishArticleHandler
{
private readonly IArticleRepository _repository;
public PublishArticleHandler(IArticleRepository repository)
{
_repository = repository;
}
public async Task Handle(PublishArticleCommand command)
{
var article = new Article(
command.Title,
command.Content
);
await _repository.AddAsync(article);
}
}
Infrastructure:
public class ArticleRepository : IArticleRepository
{
private readonly AppDbContext _context;
public ArticleRepository(AppDbContext context)
{
_context = context;
}
public async Task AddAsync(Article article)
{
_context.Articles.Add(article);
await _context.SaveChangesAsync();
}
}
API:
[HttpPost]
public async Task<IActionResult> Publish(
PublishArticleRequest request)
{
await _handler.Handle(
new PublishArticleCommand(
request.Title,
request.Content
)
);
return Ok();
}
Buradaki güzellik şu:
API'nin nasıl çalıştığı ile "geçerli bir Article nedir?" sorusunun cevabı birbirinden ayrılmıştır.

Clean Architecture kullanırken kendinize sorabileceğiniz 5 soru
Bir projeyi değerlendirirken şu sorular oldukça işe yarar:
1. Domain, veritabanını biliyor mu?
Biliyorsa bağımlılık yönünü tekrar kontrol edin.
2. Business logic controller'larda mı?
Controller'lar büyüyorsa iş kurallarını doğru katmana taşıyın.
3. Application doğrudan dış servisleri mi çağırıyor?
Bir abstraction üzerinden bağımlılığı tersine çevirmek daha uygun olabilir.
4. Test yazmak için veritabanına ihtiyacınız var mı?
Her testin database'e gitmesi mimarinin sınırlarını ve test stratejisini yeniden düşünmeniz gerektiğini gösterebilir.
5. Bir teknoloji değişikliğinde kaç katman etkileniyor?
SQL Server'ı PostgreSQL ile değiştirdiğinizde domain dosyalarının değişmesi gerekiyorsa bağımlılıklar fazla içeri sızmış olabilir.
Clean Architecture öğrenirken nereden başlamalı?
Clean Architecture'ı öğrenmenin en kolay yolu doğrudan büyük bir proje kurmak değil, küçük bir domain üzerinden düşünmektir.
Örneğin:
E-Commerce
↓
Order
↓
Discount
↓
Payment
gibi basit bir domain seçin.
Önce business rule'ları yazın.
Daha sonra:
Domain
↓
Application
↓
Infrastructure
↓
API
şeklinde dış detayları ekleyin.
Bu sırayla ilerlediğinizde mimarinin neden böyle tasarlandığını daha rahat görürsünüz.
Sık Sorulan Sorular
Clean Architecture ne işe yarar?
Clean Architecture, iş kurallarını veritabanı, framework, UI ve dış servisler gibi değişmesi muhtemel teknik detaylardan ayırmayı amaçlar. Bunun sonucunda kodun test edilmesi ve belirli teknolojilerin değiştirilmesi daha kolay hale gelebilir.
Clean Architecture sadece .NET için mi kullanılır?
Hayır. Clean Architecture belirli bir programlama dili veya framework'e bağlı değildir. .NET, Java, JavaScript/TypeScript, Python ve farklı teknolojilerle uygulanabilir.
Clean Architecture ile MVC arasındaki fark nedir?
MVC ağırlıklı olarak uygulamanın presentation tarafındaki sorumlulukları ayıran bir pattern'dir. Clean Architecture ise uygulamanın genel bağımlılık yapısını ve iş kurallarının teknik detaylardan ayrılmasını ele alır. Aynı projede MVC ve Clean Architecture birlikte kullanılabilir.
Clean Architecture küçük projelerde gerekli mi?
Her zaman değil. Küçük ve basit CRUD uygulamalarında Clean Architecture'ın getirdiği ek abstraction'lar gereksiz karmaşıklık oluşturabilir. Projenin karmaşıklığı, ömrü ve değişim ihtiyacı dikkate alınmalıdır.
Clean Architecture ile Microservices aynı şey mi?
Hayır. Clean Architecture bir uygulamanın iç yapısını ve bağımlılıklarını düzenlemek için kullanılabilir. Microservices ise sistemi birden fazla bağımsız servis olarak yapılandırmaya yönelik bir mimari yaklaşımdır. Bir microservice kendi içinde Clean Architecture kullanabilir.
Clean Architecture'da Entity Framework Core kullanılabilir mi?
Evet. Entity Framework Core genellikle Infrastructure katmanında kullanılabilir. Önemli olan domain ve application katmanlarının doğrudan EF Core'a bağımlı hale gelmemesidir.
Son söz
Clean Architecture'ın özünü onlarca klasör, interface veya proje dosyasında aramak yerine tek bir soruda aramak daha doğru:
"İş kurallarım teknolojik detaylardan bağımsız mı?"
Eğer veritabanını değiştirmek domain'i bozuyorsa, ödeme sağlayıcısını değiştirmek bütün uygulamaya yayılıyorsa veya controller'lar business logic ile doluyorsa problem büyük ihtimalle klasör yapısından daha derindedir.
İyi mimari, kodu karmaşıklaştırmak için değil, değişikliği yönetilebilir hale getirmek için vardır.
Bir sonraki projenizde bütün katmanları ezbere oluşturmak yerine önce iş kurallarını belirleyin. Sonra bu kuralları dış dünyadan nasıl koruyacağınızı düşünün.
Çünkü temiz mimarinin asıl amacı daha fazla kod yazmak değil, doğru kodun doğru yere bağımlı olmasını sağlamaktır.
Yorumlar
Yorumlar (0)
Yorumlar üyelere açık. Üye ol · Giriş yap