Vertical Slice Architecture ve CQRS & Mediator Implementasyonu

Ezgi Gökdemir
SabancıDx
Published in
13 min readJun 4, 2023

Herkese merhaba. Bu yılki Devnot Dotnet Konferansı’nda bu konuya dair bir sunum yapma şansım oldu. Sunumda aktarmaya çalıştığım bilgileri kalıcı hale getirmek adına bir makale yazısı hazırlamak istedim. Faydalı olması dileğiyle :)

Bu makalede size Vertical Slice mimarisi nedir, bize ne gibi avantajlar sağlar, Mediator ve CQRS pattern’lerini bu mimaride nasıl uygulayabiliriz, bunlardan bahsetmeye çalışacağım. Ancak bu kısma geçmeden önce aradaki farkları daha net görmek ve konuyu pekiştirmek adına geleneksel n-tier architecture nedir biraz bundan bahsedeceğim.

N-tier Architecture nedir?

N-tier arhctitecture uygulamamızı layer’lara bölen bir yazılım mimarisidir. Burada her katmanın kendi görevi kapsamında bir takım sorumlulukları vardır. Genellikle presentation, business logic ve data storage (access) olarak adlandırdığımız katmanlara ayrılır. Bu katmanlar uygulamanızın ihtiyaçlarına ve tercihlerinize göre daha fazla katmana seperate edilebilir. Ancak en standart haliyle bu şekilde kategorize edebiliriz.

N-tier Architecture

Buradaki katmanların kapsamını özetleyecek olursak;

  • Presentation layer: Kullanıcı arayüzündeki etkileşimden sorumludur, UI katmanımızdır. Bu katman web sayfalarını, bir takım arayüz kontrollerini içerir. Yine bu katmanda bazı request validation’lar da yapabiliriz (bir input değeri için iki karakter zorunlu, bir string değeri 50 karakterden fazla olmamalı gibi kontroller).
  • Business logic layer: Bu katman gelen isteklerin işlenmesinden sorumludur. Genel olarak hesaplamalar, business validasyonlar (istenen id li kayıt database’de var mı gibi), bir takım uygulama kurallarını, business rule’ları işlediğimiz katmandır.
  • Data access layer: Direkt olarak veri tabanıyla etkileşime giren, veri transferi yapabildiğimiz, CRUD işlemlemlerimizi gerçekleştirdiğimiz katmandır.

Eğer düzgün tasarlanmış n-tier mimariye sahip bir uygulama geliştirdiysek burada presentation katmanının data katmanına doğrudan erişiminin olmamasını bekleriz. Çünkü presentation katmanı bir takım kullanıcı arayüzü işlemlerinden sorumludur ve data katmanıyla arasında direkt bir ilişki olmamalıdır. Yani UI’de yapacağınız bir değişiklik direkt olarak data katmanını etkilememeli, bozmamalı. Ek olarak data katmanı bu tarz bir mimaride çok merkezi bir konumda yer alıyor. Dolayısıyla burada kullanılan database teknolojisine oldukça bağımlıyız. Ayrıca herhangi bir verisel, şematik değişiklik yapmak istediğimizde uygulama kapsamında ciddi boyutta problemlerle karşılaşabiliyor veya ilgili değişiklik için bir çok katmanda bir takım güncellemeler, eklemeler yapmak zorunda kalabiliyoruz. Özetlemek gerekirse standart n-tier mimaride yukarıdan aşağıya doğru bir derleme bağımlılığına sahibiz. Yani katmanlar arasında tightly coupled diyebileceğimiz sıkı bir ilişki var.

Clean Architecture

Zaman içerisinde bu bağımlılığı daha az sıkı(loosly coupled) hale getirmek için Dependency Inversion, Domain Driven Design gibi yaklaşımlar uygulanmaya başlanmış. Abstraction (soyutlama) kullanılarak ihtiyaç duyulan işlevleri, detay vermeyecek şekilde bir üst katmana sunmak amaçlanmıştır.

Bu yaklaşımlarla birlikte isimlendirme olarak başlarda Hexagonal Architecture olarak duyduğumuz, son yıllarda ise Clean Architecture olarak isimlendirilen yeni bir mimariyle tanıştık.

Clean Architecture

Clean architecture da özünde layer bazlı bir mimari yaklaşım, ancak n-tier mimariye göre daha modüler ve esnek bir yapıya sahip. Burada sorumluluklar daha modüler bir şekilde birbirinden ayrılır. Dolayısıyla Separation of Concerns prensibine çok daha uygundur. Burada DI sayesinde yüksek seviye modüller, düşük seviye modüllere değil soyutlamalara bağlı hale getirilmeye çalışılıyor. Dolayısıyla ihtiyaçlara ve teknolojik değişikliklere daha hızlı adapte olduğumuz yeni bir yapı sağlıyor bize.

Bu mimarideki katmanları detaylandıracak olursak;

  • Bu mimaride uygulamanın merkezinde entity’ler ve core business rule’lar yer alır. Temel uygulama fonksiyonlarının diğer external bağımlılıklarla tightly coupled bir ilişkisinin olmaması gerekir. Buradaki amaç dış katmanlardan, teknolojilerden, frameworklerden olabidiğince bağımsız bir logic oturtmak.
  • Bir üst katmanda application business rule’lar yer alır. Burada da bir takım use case’lerimizi implemente edebiliriz.
  • Bu katmanın üzerinde ise presentation layer’larımız, gateway’lerimiz yer alır. Burası artık dış dünyaya açıldığımız katmandır ve bir takım client side kontrolleri gerçekleştiririz. Bu katmanda önemli bir business logic bulunmaz.
  • En dış katmanda ise gördüğünüz gibi framework, library ve çeşitli external tool’ları içeren bir katman var. Yani en az bağımlı olmak istemediğimiz katmanı en dışta tutmamız gerekiyor. Çünkü bir framework’e ya da bir teknolojiye bağlı kalmak istemiyoruz. Bu mimari yaklaşımdaki en önemli amaçlardan biri de bu.

Son olarak örnek vermek gerekirse Onion Architecture, Clean Architecture uygulayabilmek için kullandığımız tasarım kalıplarından biridir. Belki bu şekilde daha pekiştirici bir açıklama yapmış olurum.

Clean Architecture Dosya Yapısı

Folder Structure

Burada basit bir örnek üzerinden clean architecture bir projedeki dosya yapısını göstermek istiyorum. Buradaki katmanları amacına uygun olarak çok daha fazla katmana da bölebilirsiniz. Ancak daha basit bir uygulama örneği üzerinden gidecek olursak;

  • Core katmanında entity’lerimiz ve uygulamaya özel use case’lerimiz yer alır. Repositroy interface’lerimiz bu katmanda bulunmalıdır.
  • Infrastructure katmanında database ile ilgili işlemlerimizi gerçekleştiririz ve repository interface’lerimizin implementasyonlarını burada yazarız. Burada farklı database teknolojileri kulanarak ilerleme şansımız var.
  • Shared katmanına projedeki ortak bir takım business logic’leri, helper’ları ekleyebiliriz.
  • Web katmanı ise standart presentation layer’ımızdır.

Olası Dezavantajlar

Clean architecture bir çok problemi aşmaya yönelik ve daha esnek bir çalışma alanı sunmak için tasarlanmış bir mimari yaklaşım. Ancak bu mimarinin de kendi içerisinde bir takım dezavantajları var. Bunlar kesin bir şekilde dezavantajdır denemez ancak uygulamamızın kapsamı genişledikçe yaşabileceğimiz bir takım problemlerdir.

  • Monolotik yaklaşım

İlk olarak bu mimari monolitik bir yaklaşıma sahip. Dolayısıyla kod üzerinde bir ölçeklendirme yapmak, yeni bir özellik eklemek istediğimizde her katmanda bir takım değişiklikler yapmalıyız. Örnek vermek gerekirse, bir kütüphane otomasyonu uygulamanız var ve siz yeni bir request geliştirmesi yapmak istiyorsunuz. Mesela kitap ekleme işleminiz vardı, şimdi de yazar ekleme işlemi yapmak istiyorsunuz. Bunun için UI katmanında bir DTO modeli oluşturmalısınız, gerekiyorsa request validasyonlarını burada yazmalısınız. Core kısımda domain entity’si oluşturmalısınız. Yine burada bu işi yapacak abstract metotu tanımlamalısınız. Infrastructure ya da repository katmanında bu işi yapacak abstract metotu implemente etmelisiniz. Bu şekilde her katmanda ayrı ayrı bir takım geliştirmeler yapmanız gerekli. Zaman içerisinde domain objeleriniz, bunlar özelinde yapmanız gereken işlemler arttıkça uygulamayı scale etmeniz ve zamanla uygulamanızın bakımını yapmanız daha da güçleşebilir. Çünkü katman bazlı bir genişleme söz konusu olacak.

  • Test yazımı için mock data maliyeti

Diyelim ki anemic değil ama rich domain modelde bir uygulamanız var, dolayısıyla entity bazında behavioral işlemleriniz olacak. Bu noktada unit test yazmak çok zor olmayabilir, ancak integration ya da end-to-end testler yazmak söz konusu olduğunda durum biraz daha karmaşık hale gelebilir. Çünkü integration test yaparak katmanlar arası bir kontrol yapıyorsunuz, bir akışı test ediyorsunuz. Dolayısıyla aradaki bütün iletişim trafiğini, soyutlamaları testinize dahil etmeniz gerekir. Bu da çok fazla objeyi mocklamanıza neden olabilir.

  • Gereksiz soyutlamalar yapmak

Clean architecture mimarisi karmaşıklığa yol açabilecek çok fazla soyutlama veya katmanın eklenmesine eğilimlidir. Bu anlamda zaman içerisinde gereksiz soyutlamalar eklemek, okunabilirlik anlamında kodunuzun kalitesini düşürebilir. Bu da projeye yeni dahil olanların adapte olmasını yavaşlatabilir.

  • Performans düşüklüğü

Katmanlar yeterince optimize kodlanmamışsa bu bir performans düşüklüğüne sebep olabilir. Örneğin data katmanında kompleks bir query yazdınız ya da teknoloji bazlı bir probleminiz var. Bu verilerin alınmasıyla ilgili bir gecikmeye sebep olabilir ve verinin daha üstteki katmanlara aktarılma süresi uzayabilir. Ya da katmanlarınız bir network üzerinden iletişim kuruyor olabilir. Buradaki networksel bir aksama yine performans düşüklüğü oluşturabilir. Aynı servis metotlarına çok fazla yerden erişim sağlamanız da bir lock’lamaya sebep olup uygulamanızı yavaşlatabilir.

Vertical Slice Architecture

Bu noktadan sonra ana konumuz olan Vertical Slice Architecture’dan bahsetmek istiyorum. Vertical Slice Architecture’ın fikir sahibi aslında Jimmy Bogard. Kendisi “AutoMapper, MediatR” gibi library’leri de geliştiren kişi. Buradan kendisinin blog yazılarına ulaşabilirsiniz.

Vertical Slice Architecture’ı kısaca VSA olarak ifade edelim ve bu şekilde devam edelim :)

VSA anlatımı yapılırken genelde bir pasta dilimi örneği üzerinden gidilir. Ben de burada bu tarz bir anlatım yaparak konuyu anlatmaya çalışacağım. Şimdi yukarıda bahsettiğimiz gibi clean architecture’da infrasturcture, domain, application, presentation gibi katmanlarımız vardı. Bu katmanların her birinin pastadaki bir katman olduğunu düşünelim. Normal şartlarda bir pastayı yerken önce üstteki süslemeli soslu kısmı bitirip, sonra altındaki keki, sonrasında ise alttaki pastacı kremasını yiyelim gibi bir yaklaşım sergilemeyiz. Pastanın tepesinden dik bir şekilde bir çatal batırarak bütün katmanları aynı anda yemek isteriz. VSA’deki ana mantık da bu aslında. Yani siz bir request için bütün katmanlarda ayrı ayrı geliştirmeler yapmaktansa, tüm bu işlemleri tek bir class içerisinde gerçekleştirin ve bu class artık sizin için bir feature olsun.

Feature nedir?

Peki feature nedir biraz daha detaylandıralım. Yukarıda bahsettiğimiz kütüphane otomasyonu örneği üzerinden gidelim. Diyelim ki bir Book entity’miz var ve bu entity özelinde yapabileceğimiz işlemler yani request’ler aşağıdaki gibi olsun.

VSA bize şunu söylüyor; bu request’ler için yazdığın servis metotlarını birer feature class’ına taşı ve bu request özelindeki tüm inputlarını, outputlarını, validasyonlarını, mapper’larını, database erişim işlemlerini tamemen bu feature’lar içerisinde yaz. Yani artık ben bu request işlemi için tek bir class yazacağım, bu class’ı da feature olarak adlandıracağım ve bahsettiğim tüm adımları bu feature içerisine yerleştireceğim.

Mesela burada AddBook, DeleteBook, GetAllBooks olarak tanımladığımız işlemlerin hepsi bizim için artık bir feature’a karşılık gelecek ve her biri birbirinden bağımsız olacak. Her bir feature sadece kendi görevi kapsamında bir işlem yapacak, kitap ekleme, silme, bir kitabın detayını alma gibi.

Dolayısıyla artık bir request döngüsü bizim için aşağıdaki gibi olacak.

Kullanıcı UI kısmından bir istekte bulunacak ve bu istek ilgili feature’a gönderilecek. Şimdi bu noktada şunu sorabilirsiniz; yine elimizde belli classlar(feature) var ve bizim bu class’lardaki handler’ları tetiklememiz için presentation layer’da örneğin controller’da bu class’ların bir instance’ını oluşturmamız ya da bu class’ları bir şekilde inject etmemiz gerekmez mi?

Mediator Pattern

İşte bu noktada yardımımıza mediator pattern koşuyor. Nedir peki mediator pattern?

Bu pattern, birbiriyle ilişkili nesneler arasında birebir bir ilişki kurmaktansa araya ortak bir arabulucu nesne yerleştirip iletişim trafiğini tamamen bu nesne üzerinden yapmamız gerektiğini söyler. Bu pattern anlatılırken genelde hep bir kontrol kulesi örneği verilir. Aslında ana mantığı anlatmak için bu çok güzel bir örnek. Bildiğiniz gibi uçaklar sadece kontrol kulesiyle haberleşir, birbirleriyle değil. Hangi uçak havaalanına inmeli, hangi uçak kalkışa geçmeli, anlık rota durumu vs gibi konularla ilgili sadece kontrol kulesiyle iletişimde olurlar. Mediator pattern de aslında bu mantıkla ilerlememizi söylüyor. Yani biz bir request işlemi yaptığımızda bu istek araya yerleştirdiğimiz mediator nesnesi sayesinde bizim gönderdiğimiz request tipine göre gitmesi gereken handler’a gidecek ve bizim ilgili class’ı direkt olarak kullanmamız gerekmeyecek.

Bu noktada birazdan vereceğim örneklerde mediator pattern’i uygulayabilmek için .Net özelinde bir uygulama örneği hazırladım ve bunun için MediatR Library’sinden yararlandım. Şimdi bu library’ye göre ben artık feature’ımı aşağıdaki şekilde güncelleyebilirim.

Artık bizim request modelimiz yani inputumuz bir IRequest<T> objesinden türeyecek ve sonucumuz da yine bu T objesine bağlı olacak. Response tipi ile birlikte bir request’i temsil edecek aslında. Benzer şekilde handler’ımız da IRequestHandler interface’inden kalıtım alacak ve verdiğimiz request ve response tipine göre bir feature’ı temsil edecek. Buradaki IRequest ve IRequestHandler, MediatR kütüphanesinin bize sağladığı interface’ler. Artık sağladığımız bu generic yapı sayesinde mediator objesi bizim için verdiğimiz T objesine göre ilgili feature’ın handler’ına giderek yapması gereken işlemi gerçekleştirecek ve bir response dönecek.

CQRS (Command Query Responsibility Segregation)

Şimdi de biraz CQRS pattern’den bahsedelim. Bu pattern’deki ana mantık okuma ve yazma işlemlerimizi birbirinden ayırmaktır. Okuma işlemleri için query kavramını, yazma işlemleri için ise command kavramını kullanıyoruz. Yani sistemde bir değişiklik yapacak işlemler command, geriye sadece bir sonuç döndürecek ama herhangi bir değişikliğe sebep olmayacak işlemler query olarak adlandırılır.

Bu pattern’deki en önemli avantajlardan biri de yazma ve okuma işlemleri için ayrı database’ler ve tenolojiler kullanmamıza olanak sağlaması. Mesela yazma işlemleriniz için EF Core kullanırken okuma işlemleriniz için Dapper kullanabilirsiniz. Dapper aslında micro ORM diyebileceğimiz bir teknoloji ve EF Core’a göre daha performanslı bir ORM teknolojisi. EF core bize Unit of Work gibi bazı repostiory pattern’lar sağlayabiliyor ve yazma işlemlerimizi çok daha kolay hale getirebiliyor. Ancak komplike sorgulamalar yaparken performans anlamında bir takım sıkıntılar yaşatabiliyor. Dolayısıyla Dapper bu anlamda daha esnek ve perfromanslı. Burada statik bir takım scriptlerinizi, sorgularınızı ekleyebiliyorsunuz. Bu sorgular ADO.NET’e gönderilirken herhangi bir custom query’ye çevrilmiyor ve ek bir dönüşüm yapılmadan doğrudan ADO.NET’e gönderiliyor. Ayrıca ADO.NET’den dönen sonuç bir datasete’e doldurulurken, Dapper da ise bu sonuç bir objeye bind ediliyor. Tüm bunlar performans anlamında bir kazanım demek aslında. Dolayısıyla CQRS sayesinde bu gibi ORM tercihlerini yapma şansı bizim için çok önemli.

Şimdi biz her bir request’imiz için bir feature tanımladık. Bu feature’ların yaptığı işlemler okuma, yazma, ekleme, silme, listeleme gibi işlemler olabilir. Bu noktada bizim bu istekleri get ya da post olacak şekilde kategorize etmemiz yani CQRS pattern’e uymamız çok daha kolay hale gelecek. Mesela kitap ekleme feature’ı bir post işlemi olduğu için command, kitap listeleme bir get işlemi olduğu için artık bir query’dir diyebileceğiz.

Burada şunu da söylemekte fayda var. Bildiğiniz gibi Domain Driven Design behavioral domain object mantığını yani rich domain model’i destekler. Yani DDD domain etrafında bir takım business gerekliliklerini modellemeye çalışır, böyle bir konsepte sahip. Biz de buradaki use case’leri feature bazında kategorize ediyoruz. Dolayısıyla bu konsepte uyumlu bir yaklaşım sergiliyoruz aslında. Yine CQRS ile bu modellemeyi okuma ve yazma bazında da ayırıyoruz ve tüm bu seperate edilmiş modeller arasında Mediator pattern yardımıyla iletişim sağlayabiliyoruz. Dolayısıyla DDD’nin bu konsepti, VSA, CQRS ve Meditor ile bütüncül bir şekilde örtüşüyor. Bu anlamda tüm bu yaklaşımları bir araya getirmek çok daha performanslı, maintain etmesi kolay uygulamalar geliştirmemizi sağlayabilecektir.

VSA Dosya Yapısı

Clean architecture özelinde gösterdiğimiz örneği VSA’ye uyarlayalım. Gördüğünüz gibi tek bir katman altında controller’larımız, domain’lerimiz, data katmanı ve feature’larımız var. Feature altında her bir domain için bir klasör bulunuyor. Her domain’in de kendine ait command ve query’ler için ilgili handler’ları farklı klasörler altında. Gördüğünüz gibi daha görünür ve bağımlılık açısından çok daha esnek bir yaklaşım elde etmiş olduk.

Bu kadar bahsettikten sonra şimdi de basit bir feature örneği verelim.

AddBookHandler.cs

Burada AddBookCommand ve AddBookResponse modellerimizi görüyorsunuz. Bu modeller bizim input ve outputlarımıza karşılık geliyor. Bu feature’da bir ekleme işlemi yapacağımız için yani bir command uygulayacağımız için input’umuzu AddBookCommand olarak isimlendirdik ve Mediatr’dan yararlanabilmek için IRequest objesinden kalıtım aldık. Tabi burada çıktımızın AddBookResponse modeline bağlı olduğunu kalıtım yaparken belirttik.

Gördüğünüz gibi command nesnesinde kullanıcıdan almak istediğimiz alanlar var. Yani kullanıcı yeni bir kitap eklemek istediğinde bu kitabın adını, yazarının id’sini, yayınlanma yılını bize vermeli. Response modelinde ise işlem başarılı mı değil mi şeklinde Id property’si ile kayıt eklendiyse geçerli bir kitap id’si döneceğiz.

Daha sonra AddBookHandler class’ımızı ekledik. Yine burada IRequestHandler interface’inden kalıtım aldık ve input ve output tiplerimizi belirttik. Bu interface ilgili istek için default bir handler metodu implement etmemizi istiyor. Dolayısıyla daha sonra gelen requesti handle etmemizi sağlayacak metotu yazdık. Bu metot command’ımızı parametre olarak aldı. Önce bu kayıt database’de var mı diye küçük bir validasyon işlemi yaptık, kayıt yoksa command’dan gelen bilgileri Book entity’sine map ettik ve sonrasında database’e bir kayıt işlemi gerçekleştirdik. Son olarak da geçerli id’yi döndük.

Şimdi controller’dan bu feature’a nasıl eriştiğimize bakalım.

Controller.cs

Controller tarafında ise gördüğünüz gibi sadece mediator objesi için bir injection işlemi yaptık. Uygulama ayağa kalktığında ve biz bir istek yapmak istediğimizde buradaki AddBookHandler feature’ına eklediğimiz command tipi sayesinde mediator objesi hangi handler’a gitmesi gerektiğini bilecek ve isteği oraya yönlendirecek.

VSA’in sağladığı avantajlar

Bu mimari beraberinde bize bir çok avantaj getiriyor. Bunları aşağıdaki şekilde listeleyebiliriz.

  • Refaktoring yapmaya teşvik etmesi

Feature’lardan bahsederken ne demiştik, her bir feature birbirinden bağımsız ve aralarındaki kod benzerlikleri minimum düzeyde olmalı. Ancak yine de feature’lar arasında zaman içerisinde bir kod benzerliği (code coupling) yaşanabilir, benzer businuess logicler yazabiliriz. VSA bu coupling’i olabildiğince minimize etmemizi ister. Dolayısıyla bizi periyodik olarak refaktoring yapmaya da teşvik eder.

  • Daha az conflict yaşanması

Feature bazlı çalışıldığı için merge conflict yaşama oranı çok daha düşük. Domain bazlı her bir feature’ımız farklı classlarla tanımlıdır. Dolayısıyla bir feature’da çalışan developer’ın yaptığı geliştirmeler başka bir feature’da çalışan developer’ı etkilemez.

  • Ölçeklenebilirlik (scalability) kolaylığı

N-tier architecture’da yeni bir özellik eklemek istediğimizde ya da var olan bir özelliği güncellemek istediğimizde ilgili bütün layer’ları scale etmemiz gerekirken burada sadece o feature class’ı içerisinde bir güncelleme ya da ekleme yapmamız gerekecek, çünkü hiç bir katmana bağımlı değiliz ve tek bir class özelinde çalışıyoruz.

  • Daha dokümante kod yapısı

Daha dokümante bir kod yapısı sağlar. Bakıldığı an hangi feature’ın nasıl bir görevinin olduğu bellidir. Bu feature bazındaki ihtiyaçlar yine bu feature içerisinde tanımlandığından farklı katmanlara gidip kodu inceleme ihtiyacı olmayacak. Bu özellikle projeye yeni dahil olan geliştiricilerin çok daha hızlı adapte olmasını sağlayacaktır. Benim için böyle olmuştu açıkçası :)

  • Farklı ORM teknolojileri kullanma şansı

Her bir feature’da farklı bir database teknolojisi kullanabilme şansınız var. Bir request’imizi EF Core kullanarak işlerken başka bir request’deki işlemi Dapper’la gerçekleştirebiliriz. Belli tanımlamalar için NoSQL bir teknoloji kullanmak isteyebiliriz gibi. Normalde mikroservislerde farklı teknolojiler kullanılabiliyorken, bu yaklaşımla bunu kod düzeyine indiriyoruz. Bu da bize ihtiyaca yönelik daha esnek olabilme şansı veriyor.

  • Planlamalarda işleri daha doğru eforlayabilme

Yine sprint planlamalarınızda alacağınız maddeleri size’larken daha doğru tahminler yapmanızı sağlar. Çünkü bir feature özelinde çalışacaksanız aslında yapmanız gereken iş, eklemeniz gereken modeller vs. daha nettir. Çünkü mevcut feature da ya da eklenecek yeni bir feature’da yapılacak tüm adımlar gözünüzün önündedir.

  • Test yazım kolaylığı

Yine VSA’de uçtan uca testler yazmanız çok daha kolay hale geliyor. Çünkü artık çok daha az bağımlılığa sahipsiniz. N-tier’da bunu yaparken bağımlı olduğunuz tüm abstract classları mock’lamak durumundasınız ki gerçek senaryoyu test edebilin. VSA’de ise testini yazmak istediğiniz feature’ın aldığı command ya da query modelini mock’lamanız sizin için yeterli olacak. Sonrasında da test etmek istediğiniz handler’ı tetikleyeceksiniz. Çünkü artık input, output modeliniz, validasyonlarınız, mappingleriniz, database erişim işlemleriniz, özetle bu feature’ın ihtiyacı olabilecek her şey yine bu class’ın içerisinde. Aşağıda bununla ilgili bir kod örneği vermek istiyorum.

Integration Test

Başarılı şekilde bir kitap ekleme işlemi ve geçerli bir id alma senaryosu için bir test yazdık ve gördüğünüz gibi bir kaç adımda bu akışı test edebildik. Bunun için sadece bir command modeli mock’lamamız ve ilgili handler’a gidebilmek için bir mediator objesi inject etmemiz yeterli oldu.

Ek olarak VSA sayesinde daha hedef odaklı testler yazabiliriz. Çünkü o feature tek bir amaca hizmet ediyor. Bir şey ekleme, bir şey çıkarma, herhangi bir filtreye göre bir veri listesi çekmek gibi.

Özetlemek gerekirse, bu makalede salt bir şekilde n-tier mimariyi yermek değil, Vertical Slice Archtiecture’ın bize sağladığı tüm bu avantajların altını çizmek istedim. Tabi ki tercihlerinize ve ihtiyaçlarınıza göre monolitik çalışmak mı, n-tier bir yapıyı benimsemek mi, yoksa VSA kullanmak mı sizin için daha avantajlı buna siz karar vermelisiniz. Bu mimari yaklaşımlardan hangisi uygulamanızın kapmasına daha uygun olacaksa buna göre ilerlemelisiniz.

Vertical Slice örneğinde kullandığım uygulamanın reposuna buradan ulaşabilirsiniz.

Umarım faydalı bir içerik olmuştur. Şimdiden keyifli okumalar :)

--

--