Modern yazılım mühendisliğinin gelişen manzarasında, sadece ölçeklenebilir değil aynı zamanda tarihsel bütünlüğü de koruyan sistemlere olan talep hiç olmadığı kadar yüksek. Geleneksel CRUD (Oluştur, Oku, Güncelle, Sil) mimarileri genellikle denetlenebilirlik, ölçeklenebilirlik ve karmaşık alan modelleri konusunda zorlanmaktadır. İşte tam da burada Komut Sorgu Sorumluluk Ayrımı (CQRS) ve Olay Kaynağı (ES) kombinasyonu ön plana çıkar. Okuma ve yazma işlemlerini ayırarak durum değişikliklerini değişmez olaylar dizisi olarak ele alan geliştiriciler, yüksek işlem hacmini işleyebilen ve sistem davranışının tam bir geçmişini koruyan sağlam dağıtık veritabanı mimarileri oluşturabilir.
Temel Paradigmaları Anlamak
Bu desenin gücünü takdir etmek için, önce bu iki kavram arasındaki farkı anlamamız gerekir. CQRS, komut tarafını (veri yazma) sorgu tarafından (veri okuma) ayırır. Bu, her iki tarafın da bağımsız olarak optimize edilmesine olanak tanır; örneğin, optimize edilmiş bir ilişkisel veritabanına yazarken, okuma işlemlerini normalleştirilmemiş bir NoSQL deposundan veya bir arama motoru indeksinden gerçekleştirebilirsiniz. Olay Kaynağı, bir birleşimin durumunun mevcut bir anlık görüntüsünden değil, durum değişikliklerine neden olan olayların bir günlüğünden türetildiğini ileri sürerek bu yaklaşımı bir adım ileri taşır.
Bunun yerine status: "shipped" depolamak yerine, bir OrderShippedEvent olayı depolarsınız. Mevcut durumu bilmek istiyorsanız, olayları yeniden oynatmanız gerekir. Bu yaklaşım doğal bir denetim izi sağlar ve neyin hangi sırada gerçekleştiğini görebileceğiniz için hata ayıklamayı kolaylaştırır.
Olay Deposunun Uygulanması
Herhangi bir Olay Kaynağı sisteminin temeli Olay Deposudur. Bu, olayları kalıcı hale getiren özelleştirilmiş bir ekleme-only (append-only) günlüktür. Dağıtık bir ortamda, doğru arka ucu seçmek hayati önem taşır. İlişkisel veritabanları işe yarasa da, performans ve dayanıklılık garantileri nedeniyle AxonIQ Event Store veya hatta Apache Kafka (bir olay günlüğü olarak kullanıldığında) gibi özel olay depoları genellikle tercih edilir.
Bir olay deposu uygularken tutarlılık her şeyden önemlidir. Olayların doğru sırada depolandığından ve aynı birleşime yönelik eşzamanlı yazımların veri bozulmasına neden olmadığından emin olmalısınız. İşte bir olayın JSON tabanlı bir belge deposunda nasıl yapılandırılacağına ve kalıcı hale getirileceğine dair basitleştirilmiş bir örnek:
// Olay kaydetmek için sahte kod
class EventStore {
async saveEvent(aggregateId, eventId, eventType, payload, version) {
const event = {
aggregateId,
eventId,
type: eventType,
data: payload,
timestamp: new Date().toISOString(),
version: version,
correlationId: generateCorrelationId()
};
// Eşzamanlı değişiklikleri önlemek için iyimser kilit kontrolü
const currentVersion = await this.getVersion(aggregateId);
if (currentVersion !== version) {
throw new ConcurrencyException("Aggregate değiştirildi.");
}
await this.appendLog(event);
return event;
}
}
Komutları ve Sorguları Köprülemek
Bir CQRS uygulamasında, bir komut verildiğinde (örneğin, ShipOrderCommand), bu komut bir komut işleyicisi tarafından işlenir. Bu işleyici, birleşim durumunu günceller, bir veya daha fazla olay oluşturur ve bunları olay deposuna kaydeder. Kritik nokta, okuma modelini doğrudan güncellememesidir. Bunun yerine, olayları yayımlar ve bunlar daha sonra okuma modeli güncelleyicileri tarafından tüketilir.
Yazma tarafı ile okuma tarafı arasındaki bu asenkron iletişim, nihai tutarlılığı (eventual consistency) getirir. Bu bir dezavantaj gibi görünse de, sistemin yatay olarak ölçeklenmesine olanak tanıyan genellikle bir özelliktir. Okuma modeli, herhangi bir zamanda olay günlüğünden yeniden oluşturulabilir; bu da verinin nasıl indeksleneceği ve sorgulanacağı konusunda esneklik sağlar.
// Komut İşleyici Örneği
class ShipOrderHandler {
async handle(command) {
const order = await this.orderRepository.findById(command.orderId);
// İş kuralı doğrulaması
if (!order.isPaid) {
throw new BusinessRuleViolation("Sipariş ödenmiş olmalıdır.");
}
// Durum değişikliğini uygula ve olay oluştur
order.ship();
const events = order.pullUncommittedEvents();
await this.eventStore.saveAll(events, order.version);
}
}
Pratik Düşünceler ve Zorluklar
Olay Kaynağı ve CQRS önemli avantajlar sunsa da, artan bir karmaşıklık getirir. Geliştiriciler, okuma modelinin olay akışının türetilmiş bir görünümü olduğu "projeksiyon" kavramıyla rahat olmalıdır. Projeksiyonların yeniden oluşturması kaynak yoğun olabilir, bu nedenle indeksleme ve önbellekleme stratejileri konusunda dikkatli planlama yapılmalıdır. Ayrıca, gelişen olay şemalarının yönetimi yaygın bir zorluktur. İş mantığınız değiştikçe, olaylarınız dikkatlice sürümleendirilmeli ve performansı korumak için genellikle geçiş komut dosyaları veya anlık görüntü stratejileri gerektirebilir.
Sonuç
Dağıtık veritabanı mimarilerinde Olay Kaynağı ve CQRS uygulamak sihirli bir çözüm değildir, ancak ölçeklenebilirlik, denetlenebilirlik ve karmaşık alan mantığına yönelik belirli sorunları çözmek için güçlü bir araçtır. Sorumlulukları ayırarak ve değişmezliği benimseyerek ekipler, sadece dayanıklı değil aynı zamanda değişen iş gereksinimlerine de uyum sağlayabilen sistemler oluşturabilir. Veritabanı mühendisliği becerilerini bir üst seviyeye taşımak isteyen ara ve ileri düzey geliştiriciler için bu desenleri ustalaşmak, gerçekten sağlam dağıtık sistemler mimarisi için temel bir adımdır.