Modern kurumsal uygulamalar iki katmanlı bir zorlukla karşı karşıyadır: karmaşık ve durum ağırlıklı yazma işlemlerini yönetirken aynı anda yüksek verimli, esnek okuma sorgularını desteklemeleri gerekir. Geleneksel CRUD (Oluştur, Oku, Güncelle, Sil) mimarisi genellikle bu yük altında zorlanır; bu da sıkı bağlılık, performans darboğazları ve kırılgan veri bütünlüğüne yol açar. İşte tam da burada CQRS (Komut Sorgu Sorumluluk Ayrımı) ve Olay Kaynağı (Event Sourcing) ön plana çıkar.
Sık birlikte tartışılsa da, bu desenler farklı sorunları çözer. CQRS, durumu güncelleme mantığını (komutlar) durumun alınması mantığından (sorgular) ayırır. Olay Kaynağı, durumun nasıl kalıcı hale getirildiğini değiştirir—mevcut durumu depolamak yerine, her değişikliği açıklayan bir olay dizisi depolar. Birlikte, sağlam, denetlenebilir ve ölçeklenebilir sistemler oluşturmak için güçlü bir kombinasyon oluştururlar.
CQRS ile Okuma ve Yazma İşlemlerinin Ayrıştırılması
Standart bir mimaride, hem okuma hem de yazma ihtiyaçları için tek bir veritabanı şeması kullanılır. Gereksinimler değiştikçe, yazma modelleri sıkı tutarlılık kurallarıyla karmaşık hale gelirken, okuma modelleri performans için esnek dizinleme ve normalizasyon dışı bırakma gerektirir. CQRS, ayrı modeller tanıtarak buna çözüm getirir.
Bir banka uygulamasını düşünün. Bir işlemi yazmak, titiz doğrulama, eşzamanlılık kontrolleri ve anında tutarlılık gerektirir. Hesap bakiyelerini okumak ise bir dashboard görünümü için birden fazla kaynaktan verileri birleştirmeyi gerektirebilir. Bunları ayırarak her modeli bağımsız olarak optimize edebilirsiniz.
Bir CQRS işleyici yapısının kavramsal temsili şöyledir:
class TransferMoneyCommandHandler {
constructor(eventStore, accountRepository) {
this.eventStore = eventStore;
this.accountRepository = accountRepository;
}
async execute(command) {
// 1. Aggregate kökünü getir
const account = await this.accountRepository.getById(command.SourceAccountId);
// 2. İş mantığını uygula
account.transfer(command.Amount, command.DestinationAccountId);
// 3. Yeni olayları depola (durumu değil)
const events = account.getUncommittedEvents();
await this.eventStore.append(events);
// 4. Okuma modelini asenkron olarak güncelle
this.publishEventsToReadModel(events);
}
}
Olay Kaynağı ile Denetim İzleri Oluşturma
Olay Kaynağı, CQRS'nin "yazma" tarafını bir adım öteye taşır. Bir nesnenin mevcut durumunu kaydetmek yerine, gerçekleşen olayların bir listesini kaydedersiniz. Mevcut durum, bu olayların yalnızca bir yansımasıdır.
Bu yaklaşım, içsel olarak denetlenebilirlik sağlar. Değişiklikleri izlemek için ayrı tablolar oluşturmanıza gerek yoktur; tarih verinin kendisidir. Bir kullanıcının girişinden bir fiyat değişikliğine kadar her eylem, değiştirilemez bir olay olarak kaydedilir. Bu, yasal uyumluluk ve karmaşık iş akışlarının hata ayıklaması için kritik öneme sahiptir.
Örneğin, bir kullanıcının sipariş durumunun yanlış olduğunu iddia etmesi durumunda, tam olarak ne olduğunu, ne zaman olduğunu ve kim tarafından yapıldığını görmek için o sipariş kimliği için tüm olay geçmişini yeniden oynatabilirsiniz.
Durum Yeniden Oluşturma ve Yansıtma (Projection)
Bu mimarideki en önemli değişikliklerden biri okuma işlemlerinin yönetimidir. Yazma deposu yalnızca olaylar içerdiğinden, sorguları verimli bir şekilde yanıtlamanın bir yoluna ihtiyacınız vardır. Bu, projections (yansıtma) aracılığıyla yapılır. Yansıtma, olaylara abone olan ve sorgulamaya optimize edilmiş normalizasyon dışı veri depolarını güncelleyen okuma modelleridir.
Tarihe göre sıralanmış son işlemlerin bir listesini görüntülemeniz gerektiğini hayal edin. Ham olay günlüğünü sorgulamak (ki bu maliyetlidir) yerine, TransactionCreatedEvent için dinleyen ve ilgili verileri özel bir Okuma Veritabanına (Elasticsearch veya bir NoSQL deposu gibi) yazan bir yansıtmanız vardır.
Bu ayrıştırma, sisteminizin yatay olarak ölçeklenmesine olanak tanır. Yazma performansını etkilemeden yüksek sorgu hacimlerini işlemek için daha fazla okuma kopyası ekleyebilirsiniz.
Pratik Düşünceler ve Zorluklar
Güçlü olsalar da, CQRS ve Olay Kaynağı karmaşıklık getirir. Sonlu tutarlılığı yönetmeniz, bir yazma komutundan sonra okuma modelinin sonlu olarak güncellendiğinden emin olmanız gerekir. Ayrıca, olay şeması evrimini yönetmek için sağlam araçlara ihtiyacınız vardır; iş mantığınız değiştikçe, olay şemanız, geçmiş veri yeniden oynatma yeteneklerini bozmadan adapte olmalıdır.
Ayrıca, durum veritabanında doğrudan görünmediğinden hata ayıklama daha zor olabilir. Ancak, yüksek ölçeklenebilirlik, sıkı denetim izleri ve karmaşık alan mantığı gerektiren sistemler için bu ödün değerlidir.
Sonuç
CQRS ve Olay Kaynağı, sihirli değnekler değildir, ancak belirli mimari zorluklar için temel araçlardır. Geliştiricilerin, yalnızca ölçeklenebilir ve performanslı değil, aynı zamanda değiştirilemez tarihleri sayesinde derinlemesine içgörülü sistemler oluşturmalarını sağlarlar. Karmaşık iş akışlarıyla uğraşmak isteyen orta ve ileri düzey geliştiriciler için bu desenleri ustalaşmak, mimari olgunluğa giden kritik bir adımdır.