CQRS (Command Query Responsibility Segregation), okuma ve yazma işlemlerini ayrı modeller üzerinden yönetme deseni. Popülerliğine rağmen, her projede kullanılması gereken bir standart değil; ne zaman gerçekten değer kattığını anlamak önemli.
Temel Fikir: Okuma ve Yazma Farklı İhtiyaçlardır
Bir sipariş oluşturmak (yazma) karmaşık iş kuralları gerektirebilirken, sipariş listesini göstermek (okuma) sadece hızlı, düz bir veri projeksiyonu istiyor. Aynı modeli her ikisi için de kullanmak, ya yazma tarafını basitleştirip iş kurallarını zayıflatıyor ya da okuma tarafını gereksiz karmaşıklaştırıyor.
MediatR ile Basit Bir CQRS Uygulaması
public record CreateOrderCommand(Guid CustomerId, List<OrderItemDto> Items) : IRequest<Guid>;
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, Guid>
{
public async Task<Guid> Handle(CreateOrderCommand request, CancellationToken ct)
{
var order = Order.Create(request.CustomerId, request.Items);
await _repository.AddAsync(order);
return order.Id;
}
}
Bu yapı, her use case'i kendi Command/Query ve Handler'ına ayırarak, controller'ları ince tutuyor ve iş mantığını tek bir yerde topluyor.
Ayrı Okuma Veritabanı: Ne Zaman Gerekli?
CQRS'in en ileri hali, okuma ve yazma için tamamen ayrı veritabanları kullanmak (event sourcing ile birlikte sık görülür). Bunu, sadece çok yüksek okuma trafiği olan ve okuma modelinin yazma modelinden ciddi şekilde farklılaştığı projelerde uyguluyorum; çoğu projede tek veritabanı üzerinde Command/Query ayrımı yeterli oluyor.
Ne Zaman Gereksiz Karmaşıklık Katıyor?
Basit bir CRUD uygulamasında CQRS uygulamak, aynı işi yapan iki kat daha fazla dosya yazmak anlamına gelebiliyor. Küçük, basit domain'lerde klasik bir servis katmanı genelde daha az karmaşıklıkla aynı sonucu veriyor.
Sonuç
CQRS, karmaşık iş kurallarının okuma ihtiyaçlarından net şekilde ayrıldığı projelerde gerçek değer katıyor. Her projeye otomatik olarak uygulanacak bir "en iyi pratik" değil, bilinçli bir mimari tercih olarak ele alınmalı.