Clean architecture nedir?

Clean architecture, iş kurallarını dış dünyadan izole eden, veritabanı ve framework gibi ayrıntıları kenara iten, uzun ömürlü ve test edilebilir yazılım tasarım yaklaşımıdır; ancak küçük ve kısa ömürlü projelerde çoğu zaman gereksiz karmaşıklık üretir.

Bu yaklaşımda kodu sadece katmanlara ayırmazsınız, aynı zamanda bağımlılıkların yönünü de bilinçli şekilde belirlersiniz. Controller, UI ya da API katmanı domain kurallarını kullanır ama tam tersi geçerli olmaz. Ben clean mimariyi, iş kurallarını koruyan bir çekirdek gibi düşünürüm. Dış çeperde frameworkler, veritabanı, mesaj kuyruğu, üçüncü parti servisler bulunur. İçeride ise değişmemesi gereken kurallar yaşar.

Bu mimarinin en kritik amacı, iş kurallarını frameworklerden bağımsız tutmaktır. Yani .NET, Spring, Django, React değişebilir ama domain mantığınız ayakta kalır. Bu sayede ekipler veritabanını, mesajlaşma kütüphanesini ya da presentation katmanını daha az riskle değiştirebilir. Ayrıca test yazarken gerçek veritabanına bağlanmadan da iş kurallarını doğrulayabilirsiniz.

Sık karıştırılan nokta, bunun sihirli bir performans reçetesi olmadığıdır. Bu bir düzen ve bağımlılık yönetimi tekniğidir. Kodunuzu daha okunabilir, daha taşınabilir ve daha uzun vadeli hale getirmeyi hedefler. Fakat her projeye aynen uygulamak zorunda değilsiniz. Özellikle tek geliştiricili, basit CRUD uygulamalarında bu yapı gereğinden fazla dosya ve katman yaratır.

Temel ilkeler

Yaklaşımın kalbinde birkaç temel ilke yer alır. Önce bağımlılıkların yönünü kontrol edersiniz. Ardından iş kurallarını arayüzler üzerinden dış dünyaya açarsınız. Bu yaklaşımda şu prensipler öne çıkar:

  • İş kuralları framework bağımsız kalır.
  • Bağımlılıklar daima içe doğru akar.
  • Arayüzler iç katmanda, implementasyon dış katmanda bulunur.
  • Test edilebilirlik ve değişime açıklık önceliklidir.

Bu ilkeler, klasik katmanlı architecture yaklaşımını daha sıkı kurallarla yeniden yorumlar. Amaç, domain mantığını merkeze koyan net bir sınır çizmektir.

Bağımlılık kuralı

Yaklaşımın en kritik noktası bağımlılık kuralıdır. Bu kurala göre hiçbir iç katman, dış katmanlardan referans almaz. Yani domain katmanı infrastructure kodunu bilmez. Ancak infrastructure katmanı domain arayüzlerini tanır. Böylece iş kurallarını, veritabanı ya da framework değişikliklerinden korursunuz.

Clean architecture katmanları

Yaklaşımı pratikte anlamak için katmanlara bakmak gerekir. Çoğu örnekte merkezde domain katmanı yer alır. Onu application katmanı, ardından infrastructure ve en dışta presentation takip eder. İsimler değişebilir ama bağımlılık yönü değişmez. İçerideki halka dışarıyı tanımaz, dışarıdaki halka içeriği bilir.

Domain katmanı iş kurallarını ve entity tanımlarını içerir. Application katmanı use case mantığını, iş akışlarını ve orkestrasyonu üstlenir. Infrastructure katmanı ise veritabanı, dosya sistemi, HTTP istemcileri gibi dış bağımlılıkları barındırır. En dıştaki presentation katmanı da API controller, UI ya da mesaj tüketicilerini tutar. Bu yapı, core iş mantığını merkeze alır.

Teknik liderler genelde “Bu katmanları ekibime nasıl anlatırım?” diye düşünür. Burada somut bir adım listesi işe yarar:

  1. Önce domain katmanında entity ve kuralları tanımlayın.
  2. Sonra application katmanında use case sınıflarını oluşturun.
  3. Ardından bu use case’lerin ihtiyaç duyduğu interface tanımlarını ekleyin.
  4. Infrastructure katmanında bu interface’lerin somut implementasyonlarını yazın.

Bu sıralama, ekibin bağımlılık yönünü zihninde netleştirmesine yardım eder. Özellikle .NET gibi güçlü ekosistemlerde, framework sınıflarını core katmana sokmamak önemli bir disiplindir. Mevcut bir kod tabanında bu sınırların nerede çizileceğine dışarıdan bakmak gerekiyorsa yazılım mimarisi danışmanlığı bu işin parçasıdır.

Domain katmanı

Domain katmanı, bu mimarinin en kritik halkasıdır. Burada entity sınıfları, value object’ler ve domain servisleri yaşar. Örneğin bir customer nesnesi, iş kurallarını kendi üzerinde barındırır. Bu katmanda public arayüzler tanımlar, ancak veritabanı ya da HTTP detaylarını bilmezsiniz. Domain katmanında framework bağımlılığı görüyorsanız, tasarımı yeniden gözden geçirmek gerekir.

Application katmanı

Application katmanı, use case odaklı çalışır. “Customer oluştur”, “Siparişi iptal et” gibi işlemleri burada tarif edersiniz. Bu katman domain kurallarını kullanır, ancak yine infrastructure detayına doğrudan bağlı kalmaz. Örneğin bir “CreateCustomerHandler” sınıfı, “ICustomerRepository” interface’ini kullanır. Bu yapı, test sırasında repository’yi kolayca sahte nesneyle değiştirme imkânı sağlar.

Infrastructure katmanı

Infrastructure katmanı dış dünya ile konuşan kısımdır. Veritabanı erişimi, mesaj kuyruğu entegrasyonu, üçüncü parti API çağrıları bu katmanda yer alır. Bu katman domain veya application katmanında tanımlanmış interface’leri implement eder. Söz gelimi, “ICustomerRepository” için “EfCustomerRepository” sınıfını burada yazar, Entity Framework gibi araçları bu katmana hapsedersiniz. Böylece infrastructure değişse bile core mantık sabit kalır.

Hexagonal ve onion mimarisi ile ilişkisi

Bu yaklaşımı araştıran birçok kişi, hexagonal ve onion mimariyle ilişkisini merak eder. Bu üç yaklaşım aslında aynı fikrin farklı sunumlarıdır. Merkezde iş kuralları vardır ve bağımlılıklar her zaman dıştan merkeze doğru akar. Fark, terminoloji ve görsel sunumdadır. Hexagonal mimaride port ve adapter kavramları öne çıkar. Onion mimaride ise halka yapısı daha belirgindir.

Bu mimariler arasındaki benzerlikleri ve farkları netleştirmek için kısa bir tablo faydalı olur.

YaklaşımMerkezDış KatmanOdak
CleanDomainFrameworkBağımlılık yönü
HexagonalCoreAdapterPort/adapter
OnionModelUI/DBHalka yapısı

Bu tablo, kavramların aslında aynı aileye ait olduğunu gösterir. Ben pratikte isimden çok, bağımlılık yönü ve sınırların netliğine odaklanmayı daha anlamlı buluyorum. Kod incelemesinde de çizimden çok interface konumuna ve test edilebilirliğe bakmak işe yarar.

Hexagonal mimari

Hexagonal mimari, aynı fikrin önemli bir varyantıdır. Burada core domain çevresinde port ve adapter kavramları bulunur. Port, core’un dış dünya ile konuştuğu soyut arayüzdür. Adapter ise bu port’u somut bir teknolojiye bağlayan katmandır. Diyelim ki, bir REST adapter, bir mesaj kuyruğu adapter’i ya da CLI adapter’i kullanabilirsiniz. Bu sayede aynı domain mantığını farklı kanallardan yeniden kullanırsınız.

Onion mimari

Onion mimari, halka çizimiyle bilinir. Merkezde domain modeli ve iş kuralları yer alır. Onu application servisleri, ardından infrastructure ve en dışta UI katmanı takip eder. Ekipler onion diyagramını genellikle daha görsel bulur. Ancak temel fikir aynıdır. İç halka dış halkayı tanımaz. Dış halka içeriye referans verir. Bu sayede, core mantığı izole tutarsınız.

Küçük projelerde gereksiz karmaşıklık

Clean architecture'ı benimseyenler bazen önemli bir noktayı atlar: Her projede bu kadar katmana gerçekten ihtiyaç duymayabilirsiniz. Özellikle tek modüllü, basit CRUD tabanlı, kısa ömürlü iç uygulamalarda bu yapı ciddi dosya kalabalığı yaratır. Bir customer kaydı için dört farklı katmana sınıf açtığınızda, ekip küçükse yönetim maliyeti artar.

Ben küçük projelerde, katman sayısını azaltmayı daha mantıklı buluyorum. Mesela, domain ve application katmanını tek bir core katmana toplayabilirsiniz. Infrastructure ve presentation katmanını da daha ince tutarsınız. Katman sayısını tartışmak yerine, “Bu projede hangi bağımlılıkları izole etmek gerçekten kritik?” sorusunu sormak daha doğrudur.

Gereksiz karmaşıklık yaratan tipik işaretleri şöyle sıralayabiliriz:

  • Her entity için üçten fazla katmana dağılmış sınıf açmanız.
  • Use case sayısının çok az, boilerplate kodun çok fazla olması.
  • Test yazılmadığı halde hâlâ ağır bir mimariyi sürüklemeniz.
  • Geliştiricilerin dosyayı bulmak için sürekli arama yapması.

Bu sitenin de üzerinde çalıştığı içerik yönetim sistemimiz YouCMS'te clean architecture'ı dört katmanla, yani sunum, uygulama, domain ve altyapıyla kurdum; CQRS'i ise bilerek almadık, işler sade servis sınıflarında yürüyor. Katman sınırının ne kazandırdığını en net önbellek temizliğinde gördüm. Önceden önbelleği yönetim ekranlarının controller'ları kendisi temizliyordu. Bu işi servis katmanına taşırken genel bir “şu önbellekleri sil” mesajını bilerek reddettik: uygulama katmanı yalnız ne olduğunu, örneğin “içerik değişti” bilgisini yayınlıyor, hangi önbelleğin silineceğine sunum katmanı karar veriyor. Böylece önbellek düzeni değiştiğinde servislere dokunmak gerekmiyor ve servisi çağıran her yeni ekran temizliği kendiliğinden alıyor.

Yöneticiler özellikle ekip büyüklüğünü ve proje ömrünü hesaba katmalıdır. Çok küçük bir ekipte, her değişiklik için dört dosyayı birlikte güncellemek hızınızı düşürür. Bu durumda, domain kurallarını net tutan ama daha az katmanlı bir architecture çoğu zaman daha verimlidir.

Uygulama örnekleri

Basit bir envanter takip aracı düşünün. Sadece birkaç tablo, temel CRUD operasyonları ve sınırlı kullanıcı var. Bu senaryoda clean architecture rehberlerini birebir uyguladığınızda, her tablo için domain, application, infrastructure ve presentation katmanında ayrı dosyalar açarsınız. Ancak proje hiçbir zaman genişlemeyecekse, bu yapı ekip için gereksiz bir yük haline gelir. Aynı işi, daha sade bir katman yapısıyla da güvenli biçimde yürütebilirsiniz.

Kod örnekleri

Basit bir C# örneği üzerinden gidelim. Domain katmanında bir interface tanımlarsınız:

public interface ICustomerRepository { Customer GetById(Guid id); }

Infrastructure katmanında ise bunu implement edersiniz:

public class EfCustomerRepository : ICustomerRepository { /* EF kodu */ }

Küçük projede bu iki katmanı tek bir core katmana da koyabilir. Önemli olan, ileride değişmesi muhtemel parçaları net görmenizdir. Eğer veritabanını değiştirme ihtimaliniz yoksa, fazladan soyutlama yerine sade bir repository sınıfı çoğu zaman daha pratiktir.

Sık sorulan sorular

Clean architecture ile katmanlı mimari arasındaki fark nedir?

Clean architecture klasik katmanlı mimariyi daha sıkı bağımlılık kurallarıyla yeniden yorumlar. Katmanlı yapıda çoğu zaman presentation katmanı doğrudan data access katmanına referans verir. Clean yaklaşımda ise domain ve application katmanı merkeze alınır, diğer her şey bu çekirdeğin etrafına dizilir.

Hexagonal mimari aynı şey mi?

Hexagonal mimari clean architecture ile aynı aileye ait, ancak terminolojisi farklı bir yaklaşımdır. Hexagonal, port ve adapter kavramlarını öne çıkarır. Clean mimari ise katman isimleri üzerinden düşünmeyi kolaylaştırır. Her ikisi de domain merkezli tasarımı ve bağımlılıkların içe doğru akmasını savunur.

Clean architecture test yazmayı nasıl etkiler?

Clean architecture, iş kurallarını frameworklerden ayırdığı için test yazmayı ciddi biçimde kolaylaştırır. Domain ve application katmanında saf C# ya da saf core kod kullanırsınız. Bu sayede veritabanına bağlanmadan, sadece interface’leri sahteleyerek hızlı birim testleri yazabilirsiniz.

Mevcut bir projeye sonradan uygulanabilir mi?

Clean architecture mevcut projelere kademeli refaktoring ile uygulanabilir. Önce domain kurallarını tespit edip ayrı bir core katmana taşırsınız. Ardından repository ve servis bağımlılıklarını interface’ler üzerinden soyutlayarak infrastructure katmanına çekersiniz. Bu süreçte her adımı testlerle desteklemek riski azaltır.

Clean architecture ne zaman tercih edilmelidir?

Clean architecture uzun ömürlü, büyüme potansiyeli yüksek ve birden fazla entegrasyona açık projelerde daha anlamlıdır. Özellikle ekip büyürken modülerlik ve bağımsız deploy ihtiyacı artıyorsa bu yaklaşım güçlü bir çerçeve sunar. Küçük, tek amaçlı uygulamalarda ise daha hafif bir mimari çoğu zaman yeterlidir.

Clean architecture'ı mimari türleri arasında konumlandırmak için yazılım mimarisi nedir, servis sınırları için mikroservis nedir yazılarına; ekip ölçeğindeki teknoloji kararları için yazılım danışmanlığı sayfasına bakabilirsiniz.