Katmanlı Mimari’ye Genel Bir Bakış (Layered Architecture)
Yazılım dünyasında en çok bilinen mimari için katmanlı yapı dersek sanırım yanılmış olmayız. Diğer bir adıyla N-katmanlı (n-tier) olarak da bilinen bu mimari yapı, Java ve C# gibi yaygın kullanım alanı olan programlama dilleri tarafından geliştirilen uygulamalar için de-facto standart olarak kullanılmaktaydı. Farklı bir açıdan bakarsak, aslında bu yapı hepimizin çalıştığı büyük şirketlerdeki organizasyon yapısı ile de birebir benzerlik göstermektedir.
Peki, nedir bu katmanlı mimari?
Katmanlı mimari yapısındaki tüm bileşenler dikey bir hiyerarşi ve farklı görev tanımları ile birbirlerinden ayrışmaktadırlar. (örnek olarak presentation veya UI katmanı, business katmanı vs). Standart olarak katman sayısı spesifik olarak belirtilmese de genel kullanım pratiği açısından 4 farklı katman ile kullanımı en yaygındır: Bu katmanlar kullanıcı (presentation veya UI), iş kuralları (business), data işlemleri (persistence) ve veritabanı (database) olarak söylenebilir. Bazı durumlarda iş akışları katmanı ile data işlemleri katmanı bir arada olabilir, bu gibi durumlarda en basit haliyle uygulamamız üç farklı katmandan oluşacaktır. Tabiki tam tersi bir durum da olabilir, uygulamamız ne kadar büyük ve karmaşık olursa görev ve sorumluluklar da o kadar fazla olacağı için aşağıdaki katmanların yanına farklı görev ve sorumluluklara sahip N tane daha katman gelebilir. Bu nedenle mimari yapımızın diğer bir adı n-tier olarak isimlendirilmiştir.

Resimden de anlaşılacağı üzere, her bir katmanın kendine özel bir rolü ve sorumluluğu bulunmaktadır. Örnek verecek olursak; kullanıcı katmanı sadece doğrudan kullanıcı etkileşimi olan yerlerde aktif olarak sorumludur ve görevi kullanıcı etkileşimini yönetmektedir. İş akışları katmanı ise uygulamamızın içeriğine göre gerekli iş akışlarını ve kuralları işletmekle görevlidir. Kullanıcı katmanından gelen isteğin içeriğine göre hangi işlemlerin yapılacağına karar vermek de yine bu katmanın görev ve sorumluluğu dahilindedir. Her bir katman yapılacak işin soyut bir parçası gibidir. Bu örnek olarak, uygulama katmanı kendinden sonra işin nasıl yapılacağıyla vs ilgilenmiyor demektir. Mesela kullanıcı ekranında bir data sorgulanmak istendi, bu katman isteği alıp kendinden sonraki katmanlara iletmekle görevlidir, datanın nasıl geleceği, nereden geleceği veya hangi kurallara göre bir iş akışından geçeceği bu katmanın bilmesi gereken konular değildir. Tek önemli nokta istediği datanın içeriği ve hangi formatta beklediğidir. Aynı mantık açısından düşündüğümüzde, iş akış katmanı da elde edilen datanın kullanıcıya nasıl görüneceğinden veya istekteki datanın nereden elde edildiği bilgisi ile ilgilenmemektedir.
Bu şekilde her katmanın sadece kendine özel bir sorumluluk alanı olmasını yazılım dünyasında seperation of concerns olarak isimlendiriyoruz. Belki de katmanlı mimarinin en önemli özelliği uygulamamızdaki bileşenlerin (component) birbirleri arasında bu şekilde bir farklılık olmasıdır. Her katmandaki bileşen sadece ait olduğu katmandaki görev ve sorumluluk ile ilgilenmelidir. Bu da kullanıcı katmanındaki bileşenlerin sadece gösterimsel işlerle ilgileneceği anlamında gelmektedir. Ya da iş akış katmanındaki bir bileşen için sadece iş kurallarının gerçekleştirimi ile alakalı aksiyonlardan sorumludur diyebiliriz. Bu tarz bir sınıflandırma yaptığımızda, görev ve sorumluluklar sıkı bir şekilde ayrıldığı için uygulamamız yeni geliştirme yapmaya kolay imkan veren, testlerimizi kolayca koşabileceğimiz ve bakımının da görece kolay olduğu bir yapıya sahip oluyor.

Yukarıdaki görselde görüleceği gibi katmanlar kapalı-açık olmak üzere sınıflandırılabilir. Bu sınıflandırma, katmanlı mimari için önemli ve bilinmesi gereken konseptlerden bir tanesidir. Kapalı katman, gelen her isteğin her bir katmandan geçerek diğerine geçmesi gerektiğini ifade etmektedir. Örnekteki akış kullanıcı katmanına gelen isteğin veritabanı katmanına kadar gidebilmesi için her katmandan geçmesinin zorunlu olduğunu göstermektedir.
Peki neden doğrudan erişime izin vermek yerine bu şekilde bir yapı ile her katmandan isteğin geçmesini zorunlu tutuyoruz? Mantıklı düşününce doğrudan erişim izni vererek uygulamamızın daha hızlı çalışmasını sağlayabiliriz ve isteğimizin gereksiz yere diğer katmanlara uğramasına da engel olabiliriz. Burada katmanlı mimarinin diğer bir önemli konsepti olan layers of isolation ile karşılaşıyoruz. Bu konsept bize herhangi bir katmanda yapılan değişikliğin, diğer katmanlardaki bileşenlere etkisi olmaması gerektiğini söylüyor. Diğer bir deyişle, yapılan değişiklik sadece ilgili katmandaki bileşenleri etkilerken uygulamamızın tamamı bundan etkilenmemelidir. Eğer doğrudan kullanıcı katmanının data katmanına erişim izni vermiş olsaydık, data işlemleri katmanında yapacağımız bir değişiklik hem kullanıcı katmanına hem de kendi katmanına etki vereceğinden dolayı uygulamamızdaki katmanlar birbirlerine bağlı olmuş olacaktır. Yazılım tasarlarken amacımız “low-coupling” uygulamalar geliştirmek olduğundan, yani birbirleri ile bağımlılığı mümkün olduğunca az olan veya bağımlı olmayan, doğrudan erişim izni vermemiz durumunda bu amaca uygun hareket etmemiş oluyoruz ve bileşenler arasında doğrudan bir bağ kurulduğu için uygulamamız “tightly coupled” oluyor. Bu da uzun vadede uygulamamızın bakımını zorlaştırıp hem zaman hem da maliyet anlamında ekstra yük getiriyor. Layers of isolation konsepti, her katmanın birbirinden ayrı olmasına doğal olarak bu katmanların birbirlerinden ya haberdar olmamaları ya da çok az bilgiye sahip olmaları anlamına da gelmektedir.
Peki gerçekten de katmanlar arasında iletişimi her zaman kapalı mı tutmalıyız, açık tutmamızı gerektiren bir durum olamaz mı? diye soruyor olabilirsiniz. Cevap, tabiki olabilir. Bu tarz bir durumda katmanımızı açık halde olacak şekilde ayarlayabiliriz. Buna örnek olarak, uygulamamıza yeni bir katman eklediğimizi düşünelim, bu katman farklı katmanlar tarafından kullanılabilen ortak servis bileşenlerini içersin. Eklediğimiz bu service katmanını muhtemelen business ile persistence katmanları arasında konumlandırmalıyız. Eklediğimiz bu yeni servis katmanını “açık” olarak ayarladığımızda bir üst katmandan gelen isteklerin servis katmanını bypass ederek bir alttaki katmana doğrudan erişebileceğini belirtmiş oluyoruz.

Açık ve kapalı katman konseptinden yararlanmak katmanlar arasındaki ilişkiyi daha doğru anlamamıza ve isteğin doğru bir akışla gitmesine emin olmamızı sağlar. Bu tarz bir durumda yazılımcı veya mimar görevinde olan birisi için mimaride kısıtlar, hangi bilgilerin diğer katmanlar ile paylaşılacağı gibi durumlar da vereceğimiz bu kararlarda çok etkilidir.
Ne zaman katmanlı mimariyi tercih etmeliyiz?
Çoğu uygulamayı tasarlamaya başladığımız zaman, katmanlı mimari bizim için güzel bir başlangıç noktası olabilmektedir. Özellikle spesifik olarak aklımızda kullanmayı düşündüğümüz bir yapı yok ise ilk olarak gittiğimiz yön genelde katmanlı mimari olarak şekillenmektedir. Peki katmanlı mimariyi seçerken nelere dikkat etmeliyiz?
Öncelikle ilk düşünmemiz ve analiz etmemiz gereken nokta architecture sinkhole anti-pattern dediğimiz isteğin katmanlar arasında gidip gelirken her katmanda ne kadar vakit harcadığı, ne kadar iş yaptığı sorusunun cevabıdır. Örnek olarak kullanıcı ekranından basit bir get isteği ile ekranda herhangi bir veriyi göstermek için istek tetiklenmiş olsun, bu istek üstten alta yani kullanıcı katmanından veritabanı katmanına kadar minimum müdahale ile gidip yanıt aldıktan sonra da yine aynı şekilde minimum etki ile cevabı kullanıcı katmanına iletiyorsa burada bu anti-pattern’i dikkatli bir şekilde düşünmeliyiz. Tabiki her isteğimiz bu şekilde olmayabilir, bazı isteklerimiz her katmanda farklı akışlara ve değişikliklere uğrayabilir. Bu durumda yapmamız gereken de isteklerimizi 80–20 kuralına göre analiz edip bu anti-pattern’e ne kadar uyup uymadığımızı kontrol etmek olacaktır. Eğer isteklerimizin %20'si katmanlardan sadece geçiş yapıp, kalan %80'i ise katmanlarda işlem görüyorsa bu normal beklediğimiz bir durumdur. Eğer analizimiz sonucu katmanlardan sadece geçiş yapan istek sayısının oranı %20'den fazla geliyorsa o zaman katmanımızı “açık” olarak nitelendirmeyi düşünmeliyiz. Bunun sonucu olarak da layer of isolation konseptine aykırı hareket etmek zorunda kaldığımız için olası değişikliklerin daha zor ve maliyetli olacağını da göz önüne almalıyız.
Sonuç
Katmanlı mimari farklı karakteristik özellikler açısından değerlendirirsek;
Agility (çeviklik) : Çeviklik uygulamamızın değişikliklere ne kadar sürede adapte olabildiği yani bir değişiklik yapmamız gereken durum olduğunda bu değişikliği ne kadar sürede yapıp hayata geçirebildiğimiz durumu ifade etmektedir. Katmanlar arasında izolasyon olduğu için görece kolay bir şekilde değişiklik yaptığımızı düşünsek de uygulamamızın monolith yapıda olması ve bileşenler arasında “tight couple” bir durum olması nedeniyle bir bütün olarak baktığımızda “düşük çevikliğe” sahip olarak nitelendirmemiz doğru olacaktır.
Ease of deployment (Deployment kolaylığı): Uygulamamız tek bir parçadan oluştuğu ve genelde büyük bir yapıya sahip olduğundan deployment açısından negatif bir durum ortadadır. Buna ek olarak yapmış olduğumuz değişiklik ufak veya basit bir değişiklik bile olsa tüm uygulamayı redeploy etmemiz gerektiği için bu da olumsuz olarak değerlendirilmektedir. Eskiden çoğumuz bu tarz bir yapıda çalıştığı için deployment süreçlerinin ne kadar uzun ve planlama gerektiği konusunda tecrübesi vardır. Ayrıca günümüzde yaygın bir şekilde kullanılan CI/CD süreçlerinde bu tarz katmanlı uygulamaların entegrasyonu ve yönetimi de zor olduğundan diğer bir negatif etki olarak yorumlayabiliriz. Bütün bu bilgiler ışığında söyleyebiliriz ki, katmanlı mimariye sahip bir uygulama deployment açısından “düşük skorlu” olarak nitelendirilmelidir.
Testability (Test edilebilirlik): Bileşenlerimiz sadece kendi katmanından sorumlu olduğundan, test yaparken diğer katmanlardaki bileşenleri mock gibi yardımcı araçlar ile varsayım değerler ile kullanarak uygulamamızı kolayca test edebiliriz. Bu nedenle test kriterleri açısından değerlendirme yaptığımızda katmanlı mimari “yüksek skorlu” olarak nitenlendirilebilir.
Performance (Performans): Bazı katmanlı mimariye sahip uygulamalar yüksek performans ile çalışsa bile genel olarak düşündüğümüzde katmanlar arasındaki haberleşmeler, gereksiz akışlar gibi durumları göz önüne aldığımızda katmanlı mimariye sahip uygulamalar performans anlamında “düşük skorlu” olarak nitelendirilebilir.
Scalability (Ölçeklenebilirlik): Uygulamamız temelde monolith bir yapıda olduğundan ölçeklenmeye uygun değildir. Diyelim ki çok fazla istek gelmeye başladı ve uygulamamız bu istekleri karşılamaya yeterli gelmedi, burada katman ve bileşen değil uygulama özelinde bir scaling yapmamız gerektiği için katmanlı mimariye sahip uygulamaların ölçeklenebilirlik yönünden “düşük skorlu” olarak nitelendirmek doğru olacaktır.
Ease of development (Geliştirme kolaylığı): Genelde tek bir programlama dilinde ve sahip olunan beceriler ile yazıldığından yazılımcılar açısından geliştirilmesi kolay olarak değerlendirilebilir. Bu nedenle katmanlı mimariye sahip uygulamaları geliştirme kolaylığı açısından “yüksek skorlu” olarak nitelendirebiliriz.
Katmanlı mimari konusunda kendime not olarak yazdığım bu yazım umarım size de yardımcı olmuştur. Diğer yazılarımda görüşmek üzere…