N-Tier Architecture Nedir?
N-tier, multi-tier veya çok katmanlı mimari günümüzde kullanılan popüler uygulama geliştirme yaklaşımlarındandır. Öyle ki bir dönem neredeyse tüm yazılım projeleri bu yaklaşımla geliştirilirdi.
Bu mimari yaklaşımda belirli sorumlulukları olan bileşenler, belirli katmanalara ayrılarak kullanılır. Verilebilecek en yaygın örnek controller, business ve data katmanlarının ayrılmasıdır. Ancak bu katmanların ayrılması tek başına bu mimariyi sağladığınız anlamına gelmez. Örnekte 3 katmandan bahsetmiş olsak da çok katmanlı mimari katman sayısının tanımını yapmaz. Uygulamanızın boyutları ve ihtiyacına göre katman sayısının değişkenlik göstermesi olasıdır.
İlettiğim gibi katmanlar belirli sorumlulukları taşımak üzere oluşturulur. Örneğin controller katmanınız gelen istekleri karşılamak ile sorumlu iken, bir başka katmanınız ise sadece veri tabanı operasyonlarını yürütüyor olabilir. Buradaki temel motivasyon ise katmanın kendi sorumluluğunu soyutluyor olmasıdır. Yine bu örnek üzerinden konuşmak gerekirse; ne data katmanı isteklerin nasıl karşılandığı ile ilgilenmelidir, ne de controller katmanı veri tabanı sorunlarını düşünmelidir. Bu mimarinin avantajları da tam olarak burada doğar. Yaklaşımımız belirli sorumlulukların belirli çizgilerin içerisinde kalmasını zorunlu kılar. Doğru bölümlenmiş ve soyutlanmış uygulamada, test ve geliştirme süreçleri iyileşir ve kompleks iş kurallarının işletilmesi kolaylaşır. Bu durum da uygulama bakımını daha kolay hale getirir.
Yukarıdaki tabloda bir çok katmanlı mimari örneği görürüz. Bunun bir e-ticaret uygulamasının diagramı olduğunu düşünelim. Ödeme yapmak istediğimizde ilgili isteği controller katmanı karşılar. Aldığı isteği business katmanına yollar. Bu katman ödeme işlemini gerçekleştirir ve sonucu data katmanına paslar. Data katmanı kendi sorumluluğunda olan akışı işleterek veri tabanına kayıtlama yapar. Nihayetinde katmanlar arasında geriye doğru bir akış gerçekleştirilerek müşteriye sürecin sonlandığına dair bir response dönülür.
Konuya bu haliyle baktığımızda akış gayet olağan duruyor. Zira her katmanın yapması gereken iş yaptığını görürüz. Controller katmanının ödeme işiyle ilgilenmediğini veya data katmanının business kısıtlarını kontrol etmediğini aklımızdan geçiririz. Fakat daha basit bir operasyon olsaydı “Acaba?” sorusunu soracağımız durumlar ortaya çıkar mıydı? Yine aynı e-ticaret sitesinde, siparişimiz için adres doldururken şehir seçtiğimizi düşünelim. Şehirlerin listesi veri tabanında bulunuyor olsun. Bu liste içinde mantık silsilesi barındırmayan bir akış ile alınabilir. Yani business katmanının alt katmanı çağırmaktan başka görevi olmayacak, sadece proxy gibi davranacaktır. Hatta örneği abartır isek; controller katmanının veri tabanına ulaşıp doğrudan sonucu dönmesi, performans açısından, diğer iki katmanın araya girip işlem yapmasını beklemektense daha avantajlı olabilir. İşte bu noktada “Acaba yanlış mı yapıyoruz?” sorusu sorulabilir. Fakat bu mimaride dikkat etmemiz gereken altın bir kural vardır: İzolasyon.
İzolasyon (veya izolasyon katmanları) kuralı, bir katmanda yapılan değişikliklerin diğer katmanları etkilememesi üzerine kuruludur. Örneğin veri tabanında yapılacak bir değişiklik sadece kendisini ve ilgili katmanı (data katmanı) etkilemelidir. Az önce üzerinden geçtiğimiz örnekte, şehir listesini controller ile veri tabanını doğrudan konuşturarak da alabileceğimizi söylemiştik. Bu şekilde zinciri kısalttığımızda, data katmanı hariç, veri tabanı değişikliklerinden etki alan ikinci bir katman daha oluşturmuş oluruz. Bu da izolasyonu doğru yapamadığımız anlamına gelecektir. Spagettileşen bu uygulamanın bakımı zor bir noktaya gidecektir. Dolayısı ile izolasyon kuralı sayesinde her katmanın kendi içinde bağımsız olması gerektiğini net şekilde anlarız. Bir uygulamayı, ana çerçeve aynı olmak üzere, değiştirme kararı aldığımızda; izolasyon ve çok katmanlılık sayesinde bu işi kolaylıkla yapabiliriz. Bunu daha net bir örnek ile açıklamak gerekirse; business katmanını yeniden yazdığınızı düşünelim ve controller katmanı ile haberleşmede kullanılan dto nesnelerinin (iki katman arasındaki kontrat sayılabilecek modeller) aynı kaldığını varsayalım. Bu durumda yepyeni bir business katmanı yazmak ve diğer katmanın etki almamasını beklemek olasıdır. Özetle çok katmanlı mimaride katmanların izolasyonu veya kapalılığı esastır.
Her ne kadar izolasyon bu mimari için gerekliyse de bazı katmanların açık veya izole olmayan durumda olmasına ihtiyaç duyabileceğimiz durumlar olabilir. Ortak servis, kütüphane veya fonksiyonlar tüm katmanlardan çağırılabilir halde olabilir. Bu durum sizde “Yine de business katmanının hemen altına yeni bir katman ekleyip izolasyon/kapalılık esasını sağlayabilirim” hissi yaratabilir ki bu durum oldukça doğaldır. Ancak “Basitçe iki sayıyı toplayan bir fonksiyon gibi genel bir fonksiyona erişmek için çok fazla katmanı dolaşmak gerekliliği ne kadar akıllıca olacaktır?” sorusu da kafamızı kurcalar. Böylece bazı katmanların açık olması veya bypass edilebilmesi konusu tekrar gündeme gelir.
Çok katmanlı mimarinin temel motivasyonu katmanlar arası sorumlulukları net bir şekilde ayırmak olduğundan, bu nokta bu mimarinin çıkmazıdır diyebiliriz. Zira katmanlar arası kontratlar (veri nesneleri — dto, entity, vo) değişmediği sürece, tabiri caizse, bir katmanı olduğu gibi söküp yerine yepyeni bir katman takmak mümkün olmalıdır. Ancak uygulama içerisindeki ihtiyaçlarınıza istinaden durum değerlendirmesi yapmanız gerekebilir. Örneğin bir ekrandaki dropdown bileşenlerin içeriğini dolduğunuz bir uygulama yazıyorsanız ve bu bileşenler kompleks veri ihtiyaçları barındırmıyorsa (veri tabanından gelen düz bir liste) akışımız controller->presentation->business->data->database zincirinden geçerken, katmanlarda sadece proxy olarak çalışan bileşenlerimiz olacaktır. Ancak aynı uygulamada karışık iş mantıkları da söz konusu olduğunda, bazı akışlarda bypass ettiğiniz katmanlar n-tier yaklaşımına aykırılık doğurmaya başlayacaktır. Dolayısı ile izolasyonu ne kadar bozacağınıza karar vermelisiniz.
Bu mimariyle geliştirilmiş uygulamaların diğer bir sorunu ise zaman içerisinde monolitikleşmeye doğru evrilmesidir. Her katmanı ayırıp deployable artifact haline getirseniz bile yanal genişlemeler (Katmanların yeni fonksiyonlar kazanması) söz konusu olacaktır. Bu durum veya sonuç olgunlaşmış uygulamalarda daha net iken, yeni uygulamalarda nispeten belirsizliğini koruyabilir. Dolayısı ile dikey bölümlenme ihtiyaçlarını da dikkate alarak geliştirme yapmak gerekir. Genel olarak bu sonuca evrilen uygulamaların deployment aşaması daha zorlu bir noktaya gelecektir. Zira bölümlenmemiş bir uygulamada yapılan küçük bir değişiklik, tüm uygulamanın deploy edilmesine sebep olacatır. Konuya tersten baktığımızda ise tek artifact içeren bir deployment süreç olarak daha basittir.
Katmanları deployable artifact olarak ayrılmamış bir n-tier uygulamada scale etme problemleri de bulunacaktır. Örneğin sadece data katmanını scale etme şansımız olmadığından tüm uygulamayı scale etmemiz gerekecektir. Alternatif olarak dikey scaling kullanılılabilir ve sisteme ek kaynak sunulabilir. Ancak zamanın büyük çoğunluğunda boşta kalan kaynakları hiç kimse görmek istemez.
Sonuç olarak çok katmanlı mimari etkilerini günümüzdeki farklı mimarilerin içinde de sürdüren bir uygulama geliştirme yaklaşımıdır. “Bir katmanın kolayca değiştirilebilmesi” motivasyonunu sağlayabilmek adına izolasyon mantığını kullanır. Bu kapalılık yaklaşımı küçük veri operasyonlarında gereksiz sayılabilecek akışlar bütünü oluşturabilir. Dolayısı ile katmanları bypass etme eğilimine gireriz. Bu eğilim izolasyonu bozar ve katman değişikliği kolaylığını ortadan kaldırır. Katmanlar arası net sorumluluk farkları ise bir işin nerede ve nasıl ele alınacağını adreslememize yardımcı olur. Bu mimariyle geliştirilmiş uygulamalar monolitikleşmeye yatkındır. Dolayısı ile dikey bölümlenmesi de düşünülerek hareket edilmesi ilerisi için iyi olacaktır. Scalability ve deployment konularında rutin bir monolit uygulamayla aynı özelliklere sahiptir.