Vertical Slice Architecture (Dikey Dilim Mimari)
Yazılım mimarileri, uygulamanın temel yapı taşlarını oluşturduğundan yazılım geliştiricileri için kritik bir öneme sahiptir. Yapılan mimari seçimi, uygulamanın

Yazılım mimarileri, uygulamanın temel yapı taşlarını oluşturduğundan yazılım geliştiricileri için kritik bir öneme sahiptir. Yapılan mimari seçimi, uygulamanın nasıl ilerleyeceğini belirler. Geliştiriciler için doğru bir mimari tercihi, yalnızca performansı artırmakla kalmaz; aynı zamanda sürdürülebilirlik, bakım kolaylığı ve ölçeklenebilirlik gibi avantajlar da sunar. Bu yönüyle Vertical Slice Architecture, modern yazılım geliştirme dünyasında öne çıkan yaklaşımlardan biridir.

Vertical Slice Architecture, yazılım geliştirme sürecinde uygulama bileşenlerinin yatay değil, dikey olarak bölümlere ayrıldığı bir mimari yaklaşımdır. Uygulamanın her bir dilimi, başlangıçtan sona kadar tüm katmanlarıyla (UI, iş mantığı, veri erişimi) bir arada değerlendirilir. Bu sayede geliştiriciler, bir özelliği geliştirdiklerinde tüm sistemin işleyişini daha iyi anlama fırsatı bulurlar.

Vertical Slice Architecture’ın Avantajları
Modülerlik: Her bir dikey dilim, belirli bir işlevi temsil eder. Bu sayede, projenin farklı bölümleri birbirinden bağımsız olarak geliştirilebilir.
Sürdürülebilirlik: Dikey dilimler bağımsız çalışabildiğinden, bir özellik üzerinde yapılan değişiklikler diğer bölümleri etkilemeden gerçekleştirilebilir. Bu durum, projelerin uzun vadede daha sürdürülebilir olmasını sağlar.
Kapsamlı Test İmkanları: Her bir dikey dilim, kendi test senaryolarıyla birlikte geliştirilir. Bu, yazılımın kalitesini artırır çünkü her özelliğin tamamı test edilebilir.
Kullanıcı Odaklı Geliştirme: Özellikler bağımsız olarak geliştirilebildiğinden, kullanıcı geri bildirimlerine dayalı olarak geliştirmeler daha hızlı yapılabilir.

Örnek 🚀
Örnek olarak bir product nesnesi oluşturup, bunun üzerinden ekleme ve listeleme işlemleri gerçekleştirelim.

Projenin Genel Yapısı
API projemizi incelediğinizde, bir Controller’ın olmadığını göreceksiniz. Bu durum, dikey mimari yaklaşımının bir sonucudur; çünkü bu mimari, her özelliği bağımsız birimler olarak ele alarak iş mantığını doğrudan ilgili dilimlere entegre eder. Böylece sistemin modülerliği ve sürdürülebilirliği artar.
namespace VerticalSliceArchitecture.WebApi.Entities;
public record Product(
Guid id,
string name,
int stock,
decimal price);
Product nesnemizi oluşturduktan sonra DbContext nesnesini oluşturma ve migration işlemlerini atlıyorum; bu bölüme repository’den ulaşabilirsiniz. Ardından, ürün oluşturmak için Features klasörü altında products adında bir klasör oluşturup CreateProduct sınıfını oluşturuyoruz.
using Carter;
using FluentValidation;
using Mapster;
using MediatR;
using VerticalSliceArchitecture.WebApi.Contracts;
using VerticalSliceArchitecture.WebApi.Database;
using VerticalSliceArchitecture.WebApi.Entities;
using VerticalSliceArchitecture.WebApi.Shared;
namespace VerticalSliceArchitecture.WebApi.Features.Products;
public static class CreateProduct
{
public class Command : IRequest<CustomResponse<Guid>>
{
public string Name { get; set; }
public int Stock { get; set; }
public decimal Price { get; set; }
}
internal sealed class Handler : IRequestHandler<Command, CustomResponse<Guid>>
{
private readonly AppDbContext _dbContext;
private readonly IValidator<Command> _validator;
public Handler(
AppDbContext dbContext,
IValidator<Command> validator)
{
_dbContext = dbContext;
_validator = validator;
}
public async Task<CustomResponse<Guid>> Handle(
Command request,
CancellationToken cancellationToken)
{
var validationResult = _validator.Validate(request);
if (validationResult.IsValid is false)
{
return CustomResponse<Guid>.Fail(
validationResult.Errors
.Select(x => x.ErrorMessage)
.ToList());
}
var product = new Product(
Guid.NewGuid(),
request.Name,
request.Stock,
request.Price);
await _dbContext.Products.AddAsync(product);
await _dbContext.SaveChangesAsync(cancellationToken);
return CustomResponse<Guid>.Success(product.id);
}
}
public class Validator : AbstractValidator<Command>
{
public Validator()
{
RuleFor(x => x.Name)
.NotEmpty();
RuleFor(x => x.Stock)
.NotEmpty();
RuleFor(x => x.Price)
.NotEmpty();
}
}
}
public class Endpoint : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
app.MapPost(
"api/products",
async (
CreateProductRequest request,
ISender sender) =>
{
var command = request.Adapt<CreateProduct.Command>();
var result = await sender.Send(command);
if (result.Status is false)
{
return Results.BadRequest(result);
}
return Results.Ok(result);
});
}
}
Ürün oluşturma, bir Command işlemidir çünkü veritabanına veri ekleyerek mevcut durumu değiştirebilir. Command sınıfını ekledikten sonra almak istediğimiz parametreleri belirtiyoruz. Aşağıdaki parametreler, ürün oluşturmak için yeterli olacaktır.
namespace VerticalSliceArchitecture.WebApi.Contracts;
public class CreateProductRequest
{
public string Name { get; set; }
public int Stock { get; set; }
public decimal Price { get; set; }
}
Fluent Validation kütüphanesini kullanarak gerekli doğrulama işlemlerini yapıyorum. MediatR ise uygulama katmanları arasındaki iletişimi kolaylaştırarak işlem taleplerini yönetmeme yardımcı oluyor. Bu kütüphanelerle ilgili detaylı kaynakları paylaşıyorum, inceleyebilirsiniz.
internal sealed class Handler : IRequestHandler<Command, CustomResponse<Guid>>
{
private readonly AppDbContext _dbContext;
private readonly IValidator<Command> _validator;
public Handler(
AppDbContext dbContext,
IValidator<Command> validator)
{
_dbContext = dbContext;
_validator = validator;
}
public async Task<CustomResponse<Guid>> Handle(
Command request,
CancellationToken cancellationToken)
{
var validationResult = _validator.Validate(request);
if (validationResult.IsValid is false)
{
return CustomResponse<Guid>.Fail(
validationResult.Errors
.Select(x => x.ErrorMessage)
.ToList());
}
var product = new Product(
Guid.NewGuid(),
request.Name,
request.Stock,
request.Price);
await _dbContext.Products.AddAsync(product);
await _dbContext.SaveChangesAsync(cancellationToken);
return CustomResponse<Guid>.Success(product.id);
}
}
public class Validator : AbstractValidator<CreateProduct.Command>
{
public Validator()
{
RuleFor(x => x.Name)
.NotEmpty();
RuleFor(x => x.Stock)
.NotEmpty();
RuleFor(x => x.Price)
.NotEmpty();
}
}
CQRS Pattern Nedir? MediatR Kütüphanesi İle Nasıl Uygulanır?
.NET Core - FluentValidation İle Validasyon Kontrolü Son olarak, Minimal API’mizi oluşturuyoruz. Bu süreçte kullandığım Carter kütüphanesi, Minimal API geliştirmeyi daha kolay, hızlı ve düzenli bir şekilde geliştirmemize yardımcı oluyor.
public class Endpoint : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
app.MapPost(
"api/products",
async (
CreateProductRequest request,
ISender sender) =>
{
var command = request.Adapt<CreateProduct.Command>();
var result = await sender.Send(command);
if (result.Status is false)
{
return Results.BadRequest(result);
}
return Results.Ok(result);
});
}
}
Ürün listeleme kodları, yapısal olarak ürün ekleme kodlarına benzediği için ilgili kısmı GitHub repomdan inceleyebilirsiniz.
Yorumlar
Yorumlar (0)
Yorumlar üyelere açık. Üye ol · Giriş yap