Uzun yıllardır geliştirdiğim projelerimde çoğunlukla çok katmanlı mimari anlayışını kullanmışımdır. Klasik olarak kullanılan BAL(Business Access Layer), DAL(Data Access Layer), PL(Presentation Layer) dışında bunu daha da parçalayarak aşırı çok katmanlı mimarı projeleri geliştirdim. :)

Yakın zamanda birden fazla mikro servisten oluşan bir projede Vertical Slice mimarisi ile kod geliştirmeye karar verdik. Bu süreçte ön izleme olarak n-tier architecture’a göre artıları eksikleri bence neler onları paylaşmak istedim.

N-Tier Architecture

Çok katmanlı mimariyi en basit haliyle tanımlayan görsel bu. Bunun gibi nice görseleri sizlerde görmüşsünüzdür.

N-Tier Architecture

Çok katmanlı mimari bir uygulamayı mantıksal katmanlara ayırır. Her bir katmanın sorumluluklarının ayrılması ve bağımlılıkların ortadan kalkması için kullanılan bir yöntemdir.Her katmanın sorumluluğu farklıdır. Katmanlar hiyerarşiye uygun mantıksal bir sırada kurgulanır.

Örn : Sunum katmanı(PL) direkt olarak data katmanı(DAL) ile konuşmaz. İş katmanı(BAL) ile konuşur, data katmanında ne olduğu ile ilgilenmez. Aynı şekilde data katmanıda data alışverişini BAL katmanı ile yapar direkt olarak sunum katmanı ile irtibatta olmaz.

Çok katmanlı mimari detaylı bir konu uzun bir yazı serisi ile detaylıca anlattığım yazımı inceleyebilirsiniz.

Vertical Slice Architecture

Özetle dikey mimariyi anlatan en güzel görsellerden biriside bu. Vertical Slice Architecture’ın fikir babası ise “AutoMapper, MediatR” gibi paketleri bize sunan Jimmy Bogard.

Vertical Slice Architecture

Çok katmanlı mimaride yatay bir düzende katmanların haberleşmesini sağlarken, dikey mimari ise adından da anlaşıldığı gibi dikey blok halinde haberleşiyoruz. Aslında haberleşme yerine işini direk dışarıya haber vermeden kendi içerisinde bitiriyoruz diyebiliriz. :)

Biraz daha açmak gerekirse çok katmanlı mimaride yeni bir CRUD işlemi yapmak istediğimizde bir çok katmana dokunmamız gerekiyor. Corex.Sample üzerinden örnek vereyim.

“UserRegister” adında bir endpoint ihtiyacımız var diyelim. Yani API’ye “Email,Password” parametreleri ile gelecek sonucunda bir kullanıcı kaydı oluşturacak. Oluşturduğu kayıt sonrasında da tüm User bilgilerini vermek yerine sadece “Email” bilgisi dönecek diyelim senaryomuz bu olsun.

  1. ViewModels içerisinde “Inputs” ve “Outputs” için iki ayrı class açmam gerekiyor. Bu classları açarak esneklik kazanmış oluyorum. Register işleminde girdi/çıktı değişirse kolayca adapte olabilmek için.
Input sayfadan alacağım parametreler için Output ise kayıt bittikten sonra geriye döneceğim çıktı için

2. IUserRepository içerisinde UserRegister methodunu verip sonra derived classından bu methodu impelemente ediyorum.

IUserRepository içerisinde “UserRegister” adında method var. Bu methodu “UserRepository” implemente ediyor.

3.Validation katmanında Input ve Dto(entity’ye map edilen) modellerim için validation yazmam gerekiyor.

InputValidation’da “UserRegisterInputModel” için validasyon, DtoValidation’da ise “UserDto” kayıt olmadan önce yapmamız gereken validasyonları belirtiyoruz.

4.UserRepository’ye eklediğimiz “UserRegister” methodu için operasyon birimine ekleme yapmamız gerekiyor. Yani UserDataOperation repolardan sorumlu olan operasyon.

UserDataOperation’a ekliyoruz. Çünkü repolar ile PL katmanı etkileşimde olmasın istiyoruz.

5. UserOperationManager’a son olarak “Register” methodunu ekliyoruz. Ve UserDataOperation, UserValidationOperation ile etkileşime geçerek işlemi bitiyoruz.

PL katmanı sadece UserOperationManager ile etkileşime girer

Uzun yıllardır geliştirdiğim projelerde çok katmanlı mimari anlayışını kullandım ve ekip arkadaşlarımı da bu şekilde yönlendirdim. Çok benimsediğim bu mimariden vazgeçmem çok da zor olmadı. Aslında vazgeçmedim, elbet kullanılacağı yerler olacak. Ama artık 1. sıraya Vertical Slice Architecture’ı koydum diyebilirim. Olumlu olumsuz yanlarını da ayrıca konuşacağız.

Vertical Slice ile tanışma hikayem ise uzun yıllardan beri kullanılan bir ürünün yeniden yazılma aşamasında KEMAL ELÇİ’nin yönlendirmesiyle başladı. Hem bize hem de projeye çok fazla katkı değer sağlayacağını düşünüyoruz. Bu süreçte projeyi geliştirirken tecrübe edeceğimiz bir çok artı/eski yanları olacaktır. Bunları da paylaşmaya devam edeceğim.

Vertical Slice mimarisinde her bir request bir “feature” olarak adlandırılır.
Yukarıda n-tier’da gösterdiğim bir çok katmana dokunma gereği olmadan feature’ın kendi input ve output’u belirttiği, busineess validationlarını direkt kendi içerisinde yazdığı bir yapıdan bahsediyorum. N-tier’da yatay olarak yaptığımı burada dikey olarak yapabiliyorum.

Vertical Slice Features

Vertical Slice ile birlikte “CQRS, MediatR” kullanımı oldukça kolay bir hãl alıyor. Bu yüzden mavi olarak işaretlenen “GetOrders, GetOrderDetails” talepleri bir “Query Feature”, kırmızı olarak işaretlenen “Approve Invoice, Cancel Order” ise “Command Feature” olarak adlandırılıyor.

Detaylı bir örnek proje paylaşacağım ancak öncesinde kafamızda canlanması açısından bir görsel paylaşmak istiyorum.

GetUserById

Developer olarak düşünelim. “Id” parametresi alan ve geriye “User” kaydı dönen bir aksiyon yazmamız istenildi diyelim. Bu tarzda bir talep geldiğinde aklımıza direkt olarak feature mantığı gelmeli. Yani “GetUserById” adında bir feature ihtiyacımız var.

Çok katmanlı mimaride bunu User’ın BAL katmanında “GetById” adında bir methodumuz ile karşılardık. Ancak dikey mimaride yeni bir feature olarak değerlendiriyoruz.

Request, Response ve aksiyonu alan Handler dikey bir düzende olduğundan yapacağımız tüm değişiklikler sadece bu feature özelinde olmuş oluyor. Yarın bir gün bir geliştirme geldiğinde yapacağım değişiklik sadece bu feature özelinde olduğundan eminim. Bu yüzden dikey mimaride mümkün mertebe soyut(abstract) sınıf kullanımından kaçınmamız gerekiyor.

Kullanacağımız soyut bir sınıf dışa olan bağımlılığımızı artırmış oluyor. Şimdi nasıl yani abstract yazmayalım mı? Diye düşünceler oluşacaktır, emin olun soyut sınıf yazmayı en çok sevenlerden biriside benim ancak bir zamandan sonra soyut sınıflardan türeyen sınıflar artıkça yönetimi zor bir hale dönüşüyor.

Feature yapısı bize tam hedef integration test yazma olanacağı sunuyor. Yazdığım feature benim isteklerimi karşıladı mı bunu en iyi ben biliyorum. Yaptığım değişikliğin testini hemen uyguladığımda kodun tutarlılığını rahat bir şekilde sağlayabiliyorum.

Şimdi avantajlar, dezavantajlara geçelim.

N-Tier Arcihtecture Avantajları

  1. Soyut sınıflar çok fazla kullanıldığından geliştiricilerin daha az kod öğrenip hızlı adapte olabilmesi
  2. Her katmanın görev tanımlarının keskin çizgilerle belirlenmiş olması
  3. Büyüyen projelerde n adet katman açarak esneklik sağlar.

N-Tier Arcihtecture Dezavantajları

  1. Çok fazla class library olduğundan derleme ve deployment süresinin uzun olması
  2. Yeni bir özellik ekleme sırasında bir çok katmanda çalışmanın getirdiği süre kaybı
  3. Soyut sınıflar çok fazla olduğundan geliştiricilerin gelişimini azaltması ve soyut sınıflarda yapılan düzenlemelerin büyük sorunlara yol açma ihtimalinin yüksek olması
  4. Kısa süre elde edilebilecek bir değerin katmanlar arasında katı kurallardan dolayı gereğinden fazla sürmesi.
  5. Baklava, Spagetti kod problemi bir süre sonra hangi aksiyon neredeydi, nereden nereye gidiyordu tarzında anlam karmaşaları

Vertical Slice Architecture Avantajları

  1. Class library sayısı oldukça az olacağından derleme ve deployment süresinin oldukça kısa olması
  2. Yeni bir özellik eklenildiğinde tek bir class ile geliştirme yapma imkanı sunması
  3. Her bir özellik içerisinde geliştirici özgürdür. Yapacağı değişikliğin sadece değişiklik yaptığı özelliği etkilediğinden emin şekilde rahatlıkla düzenleme yapabilmesi
  4. Feature bazında geliştirme yapıldığından CQRS yapısına uygunluk
  5. Soyut sınıfların en düşük seviye olduğu geliştirmeler yapan geliştiricinin yetkinliğinin artması

Vertical Slice Architecture Dezavantajları

  1. Soyut sınıfların en az kullanıldığı yapıda geliştiricinin yetkinliğinin önemli olması
  2. Her bir feature dışarıdan bağımsız olduğu için kod tekrarı ihtimali olabilir

--

--