Traditional N-Layer Architecture X Clean Architecture

Selamlar 🐣,

Bu yazımda, geçen hafta Devnot tarafından gerçekleştirilen Online Microservices & DDD Konferansı‘na katıldıktan sonra daha da dikkatimi çeken ve projelerimde kullanmak için hakkında araştırmalar yaptığım Clean Architecture’dan bahsetmeye çalışacağım.

Karmaşıklığı gitgide artan uygulamalardaki yapıyı yönetmek ve değişimi kolaylaştırmak adına belirli bir standarda ve düzene ihtiyaç duyulmaktadır. Bu karmaşıklığı yönetmenin bir yolu, uygulamadaki modülleri işlev ve özelliklerine göre ayırmaktır. Kodun katmanlar halinde düzenlenmesi, uygulama genelinde reusability’i arttırır ve kod tekrarlarının önüne geçilmesine yardımcı olur (Don’t Repeat Yourself). Katmanlı mimari, kodun organizasyonunu iyileştirmek için yaygın olarak kullanılan bir tekniktir. Kodun katmanlar halinde organize edilmesinin birkaç yolu vardır:

Bu katmanlar genellikle User Interface (UI), Business Logic Layer (BLL) ve Data Access Layer (DAL) olarak adlandırılır. İhtiyaç doğrultusunda katmanların sayısı çoğaltılabilir. Bu mimarinin amacı, her katmanın kendine özgü sorumluluğunun belirlenmesini sağlamaktır.

Separation of Concerns: Farklı kod sorumluluklarını aynı ( metod | sınıf| proje )‘de karıştırmaktan kaçının.

Bu mimari yaklaşımının bir dezavantajı, bağımlılıklarının yukarıdan aşağıya doğru gitmesidir.

Yani kullanıcı arayüzü, veri erişim katmanına bağımlı olan iş kurallarını içeren katmana bağımlıdır. Bu da genellikle uygulamadaki en önemli mantığı barındıran BLL’in bir veritabanına bağımlı olduğu anlamına gelir.

Clean Architecture, iş mantığını ve uygulama modelini uygulamanın merkezine yerleştirir. Arkasındaki ana prensiplerden birisi, Dependency Inversion prensibidir. İş mantığının, veri erişimine veya diğer altyapı concern’lerine bağlı olması yerine, bu bağımlılık tersine çevrilir: Altyapı ve uygulama concern’leri uygulama çekirdeğine (application core) bağımlıdır. Bu, uygulama çekirdeğinde soyutlamalar veya arayüzler tanımlayarak elde edilir. Bu mimariyi görselleştirmenin yaygın bir yolu, soğana benzer şekilde iç içe daireler kullanmaktır. (Onion Architecture)

İçteki daire, uygulamanızın Domain katmanıdır. Burası iş kurallarını (business rules — uygulamanızın yaptığı şeyin özü, kodun temel işlevselliği) belirlediğimiz yerdir. Örneğin bir e-ticaret uygulamasında sipariş verme gibi uygulamanızın çok sık yaptığı şeyi değiştirme olasılığınız olmadığından bu iş kuralları oldukça kararlı olma eğilimindedir.

Dıştaki daire Infrastructure’dır. Kullanıcı arayüzü, veritabanı, web servisleri ve framework gibi bileşenleri içerir. Bu bileşenlerin Domain’den daha fazla değişme olasılığı vardır. Örneğin, arayüzdeki bir butonun tasarımını değiştirmek gibi.

Domain ve Infrastructure arasında bir sınır oluşturulur. Böylece Domain, Infrastructure hakkında hiçbir şey bilmez. Bu, arayüz ve veritabanının iş kurallarına bağımlı olduğu, ancak iş kurallarının arayüz veya veritabanına bağımlı olmadığı anlamına gelir. Bu da onu bir plug-in mimarisi yapar. Domain için kullanıcı arayüzünün bir web arayüzü, masaüstü uygulaması veya mobil uygulama olması, verilerin SQL veya NoSQL kullanılarak mı yoksa bulutta mı saklandığı önemli değildir. Bu da gerektiği zaman Infrastructure’ı değiştirmeyi kolaylaştırır.

Burada Domain katmanı Entity ve Use Case olarak ayrılabilir ve bir Adapter katmanı, Domain ile Infrastructure katmanı arasındaki bahsettiğimiz sınırı oluşturur. Bu kavramları kısaca açıklamaya çalışalım:

  • Entities : Uygulamanın fonksiyonelliği için kritik olan iş kuralları kümesi. Diğer katmanlar ile ilgili hiçbir şey bilmezler ve bağımlılıkları yoktur.
  • Use Cases : Uygulamanın davranışını belirleyen iş kurallarıdır. Entities ile etkileşime girer ve bunlara bağımlıdır, ancak diğer katmanlar hakkında bir şey bilmezler. Onun için uygulamanın bir web sayfası mı yoksa iPhone uygulaması mı olduğu önemli değildir. Ayrıca dış katmanların kullanacağı interface ve abstract class’lar bu katmanda tanımlanır.
  • Adapters : Interface adapters olarak da adlandırılan adapters, domain ile infrastructure arasındaki tercümanlardır. Örneğin, verileri arayüzden alırlar. Use case ve entity için uygun bir biçimde yeniden paketlerler. Daha sonra çıktıyı use case ve entity’lerden alır ve bunu arayüzde görüntülemek veya bir veritabanına kaydetmek için uygun bir biçimde yeniden paketler.
  • Infrastructure : Bu katman, tüm I/O bileşenlerinin bulunduğu yerdir: Kullanıcı arayüzü, veritabanı, framework, cihazlar vb. Bu katmandaki şeylerin değişme olasılığı yüksek olduğundan, daha kararlı olan katmanlardan mümkün olduğunca uzak tutulurlar. Ayrı tutulduklarından dolayı değişiklik yapmak nispeten kolaydır.
  • Dil bağımsız — Bu mimariyi C#, Java, Python vb. ile kullanabilirsiniz çünkü herhangi bir kütüphaneye veya özel kod tabanına bağımlı değildir.
  • Veritabanı bağımsız — Kodun büyük çoğunluğu, uygulama tarafında hangi veritabanının kullanılabileceği hakkında bir bilgiye sahip değildir.
  • Arayüz bağımsız — Kullanıcı arayüzünü yalnızca UI projesi önemser. Sistemin geri kalanı bununla ilgilenmez.
  • Test edilebilir — Bu mimari kodunuzu çok daha test edilebilir hale getirebilir. Fazla bağımlılıklar içeren kodu test etmek zordur. Ancak bir plug-in mimarisine sahip olduğunuzda, bir veritabanı bağımlılığını (veya herhangi bir bileşeni) mock data ile değiştirmek daha kolaydır.

Clean Architecture aynı anda ve aynı nedenden ötürü değişebilecek sınıfların, bileşenler halinde gruplandırılması gerektiğine dayanır. İş kuralları, altyapı bileşenlerine göre daha kararlıdır ve diğer ayrıntılarla ilgilenen daha değişken olan bu altyapı bileşenleri hakkında hiçbir şey bilmemelidir. Katmanlar arasındaki sınır, katmanlar arasındaki verileri çeviren ve bağımlılıkları daha kararlı olan iç bileşenlerin yönüne işaret eden adapter kullanılarak korunur.

Bu mimari ile ilgili edindiğim bilgileri ve notlarımı faydalı olabilmek adına sizlerle de paylaşmak istedim. Buraya kadar okuduğunuz için teşekkür ederim.

Sağlıklı günler 🎈