System Design

Event Sourcing'ı Ustalıkla Öğrenmek: Durum Yönetiminde Temel Bir Değişim

Modern yazılım mimarisi alanında, geleneksel CRUD (Oluştur, Oku, Güncelle, Sil) uygulamaları genellikle karmaşık iş mantığı ve sıkı denetim gereksinimleri ile uğraşırken zorlanabilir. İşte tam bu noktada Event Sourcing (Olay Kaynağı), sadece bir desen olarak değil, temel bir paradigm değişimi olarak devreye girer. Sistemin mevcut durumunu değişiklik geçmişinden ayırarak geliştiriciler daha şeffaf, esnek ve sağlam sistemler inşa edebilir.

Event Sourcing Nedir?

Özü itibarıyla Event Sourcing, bir uygulamanın durumunun doğrudan saklanmadığı, bunun yerine bir olay dizisinden türetildiği bir mimari desendir. Bir nesnenin mevcut durumunu (örneğin, SiparişDurumu = Gönderildi) kaydetmek yerine, bir olayın gerçekleştiği gerçeğini (örneğin, SiparişGönderildi) kaydedersiniz.

Bir banka hesabını hayal edin. Geleneksel bir sistemde, veritabanı kaydındaki bakiye sütununu güncelleyebilirsiniz. Olay kaynaklı bir sistemde ise her bir para yatırma ve para çekme işleminin defteri bulunur. Mevcut bakiyeyi bulmak için bu olayları toplamanız yeterlidir. Bu yaklaşım, uygulama mantığınızı değişmez, sadece ekleme yapılabilen bir günlük haline getirir ve varsayılan olarak eksiksiz bir denetim izi sağlar.

Neden Event Sourcing Seçilmeli?

Event Sourcing'ın temel avantajı, bir varlığın durumunu herhangi bir anda yeniden oluşturabilme yeteneğidir. Bu, hata ayıklama, denetim ve yasal uyumluluk için paha biçilemezdir. Ayrıca, olaylar genellikle önemli iş kilometre taşlarını temsil ettiğinden, bu yaklaşım doğal olarak Etki Alanı Odaklı Tasarım (DDD) ile uyumludur.

Bununla birlikte, karmaşıklığı da beraberinde getirir. Veri sorgulama işlemi daha karmaşık hale gelir çünkü olay deposu (event store) okuma işlemi için değil, yazma işlemi için optimize edilmiştir. Bu nedenle Event Sourcing genellikle CQRS (Komut Sorgu Sorumluluk Ayrımı) ile birlikte kullanılır. Yazma işlemleri için olay deposunu, sorgular için ise ayrı bir okuma-odaklı veritabanını (SQL veya NoSQL deposu gibi) kullanırsınız.

Event Sourcing Uygulama Örneği

Durumun olaylardan nasıl türetildiğini göstermek için Java'da basitleştirilmiş bir uygulamaya bakalım. Bir siparişi yöneten basit bir aggregate kökü oluşturacağız.

public class Order {
    private String orderId;
    private OrderStatus status;
    private List<ObjectEvent> events = new ArrayList<>();

    // Yapıcı Metod
    public Order(String orderId) {
        this.orderId = orderId;
        this.status = OrderStatus.CREATED;
        // Başlangıç olayını kaydet
        applyEvent(new OrderCreatedEvent(orderId));
    }

    // Komut işleyicisi
    public void ship() {
        if (this.status != OrderStatus.PACKED) {
            throw new IllegalStateException("Sipariş gönderilmeden önce paketlenmelidir");
        }
        applyEvent(new OrderShippedEvent(orderId, Instant.now()));
    }

    // Mevcut duruma bir olay uygula
    private void applyEvent(ObjectEvent event) {
        events.add(event);
        event.apply(this);
    }

    // Olaylardan durumu yeniden oluştur (Event Store'dan yüklenirken kullanılır)
    public void replay(List<ObjectEvent> historicalEvents) {
        this.events = new ArrayList<>();
        for (ObjectEvent event : historicalEvents) {
            applyEvent(event);
        }
    }
}

// Tüm olaylar için arayüz
interface ObjectEvent {
    void apply(Order order);
}

// Örnek Olay
class OrderShippedEvent implements ObjectEvent {
    private String orderId;
    private Instant shippedAt;

    public OrderShippedEvent(String orderId, Instant shippedAt) {
        this.orderId = orderId;
        this.shippedAt = shippedAt;
    }

    @Override
    public void apply(Order order) {
        order.status = OrderStatus.SHIPPED;
        System.out.println("Sipariş " + orderId + " tarihinde gönderildi: " + shippedAt);
    }
}

Bu örnekte, Order sınıfı geçmişini saklamaz. Sadece mevcut durumu tutar. Siparişi kalıcı depolamadan yüklememiz gerektiğinde, geçmiş olay listesini replay metoduna geçiririz; bu da nesnenin kendini doğru bir şekilde yeniden oluşturmasını sağlar.

Zorluklar ve En İyi Uygulamalar

Güçlü bir araç olsa da, Event Sourcing bazı zorluklar getirir. Sürüm yönetimi kritik öneme sahiptir; olay şemanız değişirse geriye dönük uyumluluğu ele almanız gerekir. Ayrıca, binlerce olayı sık sık yeniden oynatmanız durumunda performans düşebilir. Bunu önlemek için geliştiriciler genellikle anlık görüntüler (snapshots) uygular; aggregate'in durumunu periyodik olarak kaydederek eski olayların yeniden oynatılmasını atlar.

Sonuç

Event Sourcing, yüksek bütünlük, karmaşık geçmiş ve raporlamada esneklik gerektiren belirli problem alanları için güçlü bir araçtır. Geliştirme sürecine ek yük getirse de, iş alanınızın eksiksiz ve değişmez geçmişine sahip olmanın uzun vadeli faydaları maliyetlerden genellikle daha ağır basar. Bu deseni anlayarak ve uygulayarak, mimarlar sadece işlevsel değil, aynı zamanda derinlemesine içgörü sağlayan sistemler inşa edebilir.

Share: