Database Engineering

Olay Kaynağı İçin Veri Modellemeyi Ustalaşmak: Projeksiyonlar ve Şema Evrimi

Olay Kaynağı (ES), uygulamanın durumunu yalnızca mevcut durum olarak değil, bir olay dizisi olarak saklayan güçlü bir mimari kalıptır. ES, denetlenebilirlik, zamansal sorgulama ve yeniden oynatılabilirlik gibi olağanüstü avantajlar sağlasa da, veri modelleme konusunda, özellikle veriyi nasıl okuduğumuz (projeksiyonlar) ve zaman içinde değişiklikleri nasıl ele aldığımız (şema evrimi) açısından önemli karmaşıklıklar getirir.

Veritabanı mühendisleri ve arka uç mimarları için, değişmez olaylar ile değiştirilebilir okuma modellerinin kesiştiği noktada ilerlemek, ölçeklenebilir dağıtık sistemler oluşturmanın en zorlu yönlerinden biridir. Bu yazı, olay kaynaklı bir ortamda sağlam projeksiyonlar oluşturma mekaniklerini ve şemaları evriltme stratejilerini incelemektedir.

Okuma Tarafı: Verimli Projeksiyonlar Tasarlama

Olay Kaynağı mimarisinde, "Yazma Tarafı" agreg köklerinden ve olaylardan oluşur. "Okuma Tarafı" ise projeksiyonlardan oluşur. Bir projeksiyon, olayları tüketen ve bunları ilişkisel bir veritabanı, bir belge deposu (MongoDB gibi) veya bir arama motoru (Elasticsearch gibi) gibi okuma için optimize edilmiş bir depoya güncelleyen bir işlemdir.

Başlıca zorluk, bu projeksiyonların yazma performansını bozmadan güncel tutulmasını sağlamaktır. Bu genellikle asenkron, nihai tutarlı sistemler aracılığıyla gerçekleştirilir.

Basit bir e-ticaret sipariş senaryosunu ele alalım. Bir kullanıcı sipariş verdiğinde, bir OrderCreated olayı yayımlarız. Bir projeksiyon işleyicisi bu olayı dinlemeli ve hızlı erişim için bir orders_view tablosuna bir kayıt eklemelidir.

class OrderCreatedHandler:
    def handle(self, event: OrderCreated):
        projection_db.insert({
            "order_id": event.order_id,
            "customer_id": event.customer_id,
            "total_amount": event.total,
            "status": "CREATED",
            "created_at": event.timestamp
        })

Bununla birlikte, gerçek dünya sistemleri karmaşık iş mantığını işlemeyi gerektirir. Tek bir olay birden fazla projeksiyonu tetikleyebilir. Örneğin, OrderCreated satış panosunu, envanter rezervasyon sistemini ve müşteri sadakat puanları sistemini güncelleyebilir. Bu işleyicilerin, yeniden denemeler veya ağ bölünmeleri sırasında yinelenen yan etkileri önlemek için idempotent (bir kez veya birden fazla çalıştırılması aynı sonucu verir) olarak tasarlanması hayati önem taşır.

Şema Evrimini Yönetme: Değişmez Tuzağı

Olay Kaynağının temel prensibi, bir olay bir kez yazıldıktan sonra değiştirilememesidir. Bu benzersiz bir sorun yaratır: İş gereksinimleri değiştiğinde uygulamamızı nasıl evriltiriz? Bir sınıfı yeniden düzenlersek veya bir alan adını değiştirirsek, tarihsel olaylar orijinal formatlarında kalır.

Olay deposunun veritabanı şemasını basitçe güncelleyemeyiz çünkü olaylar değişmezdir. Bunun yerine, sürümleme ve dönüştürme stratejisi benimsememiz gerekir. İki yaygın yaklaşım vardır:

  1. Geriye Dönük Uyumlu Değişiklikler: Eski alanları kaldırmadan olaylara yeni alanlar ekleyin. Örneğin, yeni bir kargo sağlayıcısını takip etmemiz gerekiyorsa, OrderShipped olayına bir shipping_provider alanı ekleriz. Mevcut olaylarda bu alan null olarak bulunur ve uygulama bunu zarifçe yönetir.
  2. Olay Sürümleme: Bir değişiklik kırıcı ise (örneğin, bir para birimi kodunu "USD"den "US"ye değiştirmek), OrderShippedV2 gibi yeni bir olay türü oluşturmalıyız. Sistem, hangi sürümdeki olayın işleneceğini belirlemek için mantığa ihtiyaç duyar.

Bir işleyicinin sürümlemeyi nasıl yönetebileceğine dair bir örnek:

def process_event(event):
    if event.type == "OrderShipped" and event.version == 1:
        # Eski olayı mevcut yapıya dönüştür
        normalized_data = migrate_v1_to_current(event.data)
        return save_event(normalized_data)
    elif event.type == "OrderShippedV2":
        return save_event(event.data)
    else:
        raise UnsupportedEventVersionError()

Tutarlılık İçin Olayları Yeniden Oynatma

Şema evrimi yalnızca yeni verileri okumakla ilgili değildir; genellikle eski verileri düzeltmekle de ilgilidir. Projeksiyon mantığınızda bir hata bulursanız veya okuma modelinize yeni bir alan eklemeniz gerekirse, mevcut olay akışına karşı projeksiyon işleyicilerinizi yeniden çalıştırabilirsiniz.

Bu "yeniden oynatma" yeteneği, Olay Kaynağının süper gücüdür. Tüm okuma veritabanınızı, mevcut şemayla mükemmel şekilde eşleştiğinden emin olmak için sıfırdan yeniden oluşturmanıza olanak tanır.

def rebuild_projection(event_store, projection_engine):
    # 1. Mevcut projeksiyon deposunu temizle
    projection_engine.clear()
    
    # 2. Tüm tarihsel olayları akıt
    events = event_store.get_all_events()
    
    # 3. Bunları en son işleyici mantığı üzerinden yeniden işle
    for event in events:
        projection_engine.process(event)

Sonuç

Olay Kaynağında veri modelleme, geleneksel CRUD işlemlerinden geleneksel bir zihniyet değişikliği gerektirir. Artık yalnızca tablolar tasarlamıyorsunuz; gerçeklerin bir zaman çizelgesini tasarlıyorsunuz. Değişmez olay yapılarına odaklanarak, sağlam sürümleme stratejileri uygulayarak ve olay yeniden oynatmanın gücünden yararlanarak, yalnızca dayanıklı olmakla kalmayan, aynı zamanda değişen iş gereksinimlerine de uyum sağlayan dağıtık sistemler oluşturabilirsiniz.

Temel çıkarım, şema evriminin Olay Kaynağında birinci sınıf bir vatandaş olduğudur. Olayların değişmezliğini kucaklayın, esnek projeksiyonlar tasarlayın ve bu güçlü mimari kalıbın tam potansiyelini ortaya çıkarın.

Share: