Event Sourcing
2 Haziran 2024 • ☕️ 6 dk okuma • 🏷 bilgisayar, yazılım, yazılım-mimarisi
Yazar tarafından şu dillere çevrildi: English
Event sourcing, veri yönetimi ve sistem tasarımında kullanılan modern ve etkili bir desendir. Geleneksel veri yönetimi yaklaşımlarından önemli ölçüde farklılık gösterir. Geleneksel yöntemlerde veriler genellikle son durumlarıyla saklanır; yani bir nesnenin güncel durumu doğrudan veri tabanında tutulur ve herhangi bir değişiklik yapıldığında bu son durum güncellenir. Ancak event sourcing yaklaşımında veriler bir dizi olay (event) olarak saklanır. Her olay sistemde gerçekleşen bir değişikliği veya eylemi temsil eder. Bu olaylar kronolojik bir sırayla kaydedilir ve saklanır.
Event sourcing’in temel ilkesi, bir nesnenin mevcut durumunu doğrudan saklamak yerine, bu durumu belirleyen tüm olayların bir listesini saklamaktır. Bir nesnenin durumu, bu olayların sıralı bir şekilde yeniden oynatılması (replay) ile yeniden oluşturulur. Bu sayede herhangi bir anda bir nesnenin geçmişteki herhangi bir durumuna erişmek veya belirli bir zaman dilimindeki değişiklikleri izlemek mümkün hale gelir.
Bu yaklaşımın birçok avantajı vardır. İlk olarak, veri tutarlılığı sağlanır çünkü her değişiklik bir olay olarak kaydedilir ve bu olaylar sıralı bir şekilde işlenir. İkinci olarak, sistem izlemesi ve hata toleransı artar; çünkü sistemde meydana gelen tüm değişiklikler olaylar üzerinden izlenebilir ve gerektiğinde geri alınabilir. Üçüncü olarak, denetlenebilirlik ve şeffaflık sağlanır; çünkü tüm olaylar kaydedildiğinden dolayı, sistemin nasıl ve neden bu duruma geldiği detaylı bir şekilde incelenebilir.
Event sourcing ayrıca, özellikle dağıtık sistemlerde, veri senkronizasyonu ve tutarlılığı konularında da büyük avantajlar sunar. Her olay merkezi bir olay günlüğüne kaydedilir ve bu olaylar farklı bileşenler arasında paylaşılabilir. Bu sayede sistemin farklı parçaları arasında tutarlı bir veri durumu sağlanabilir. Ayrıca event sourcing, CQRS (Command Query Responsibility Segregation) gibi diğer modern mimari desenlerle birlikte kullanıldığında, sistemin performansını ve ölçeklenebilirliğini artırabilir.
Event sourcing, modern yazılım mimarilerinde önemli bir rol oynayan, güçlü ve esnek bir veri yönetim yaklaşımıdır. Veri tutarlılığı, sistem izlemesi, hata toleransı ve denetlenebilirlik gibi avantajları sayesinde, özellikle karmaşık ve büyük ölçekli sistemlerde tercih edilen bir yöntemdir.
Temel Kavramlar
- Event (Olay): Sistem içerisinde gerçekleşen her türlü değişiklik bir olay olarak kaydedilir. Örneğin, bir kullanıcının kaydedilmesi, bir ürünün eklenmesi veya bir siparişin tamamlanması bir olaydır.
- Event Store: Olayların kalıcı olarak saklandığı yerdir. Geleneksel veritabanlarından farklı olarak, burada sadece olaylar saklanır.
- Aggregate: Bir dizi olayı temsil eden ve bu olayları işleyerek bir durumu (state) oluşturan yapı.
- Command (Komut): Sistem tarafından gerçekleştirilecek işlemleri tanımlar. Örneğin, “Sipariş Ver” bir komuttur.
- Projection (Projeksiyon): Olaylardan durumu oluşturur ve bu durumu farklı veri modellerine dönüştürür.
Event Sourcing’in Avantajları
- Denetlenebilirlik: Geçmişte ne olduğunu anlamak için tüm olaylar kaydedilir. Böylece, sistemin nasıl bu duruma geldiği izlenebilir.
- Esneklik: Yeni veri modelleri oluşturmak veya mevcutları değiştirmek daha kolaydır. Projeksiyonlar sayesinde veriyi farklı şekillerde sunabilirsiniz.
- Gerçek Zamanlı İşleme: Olaylar anında işlenebilir ve sistemin diğer parçalarına iletilebilir.
Event Sourcing ile Geleneksel Veri Yönetimi Arasındaki Fark
Geleneksel veri yönetimi, genellikle bir veritabanı modeline doğrudan erişim sağlayarak çalışır ve bu model üzerinde yapılan güncellemeleri anında uygular. Bu yaklaşımda, veritabanı tabloları belirli bir anlık durumun yansımasıdır ve her veri güncellemesi, önceki durumun üzerine yazılarak gerçekleştirilir. Bu yöntem veri tutarlılığını sağlamak ve veri işlemlerini basit ve hızlı bir şekilde gerçekleştirmek için yaygın olarak kullanılır. Ancak geçmişe dönük değişikliklerin izlenmesi veya belirli bir zaman dilimindeki verinin yeniden oluşturulması gerektiğinde bazı zorluklar ortaya çıkabilir.
Event sourcing ise tamamen farklı bir yaklaşım sunar. Bu modelde veri yönetimi, gerçekleştirilen her değişikliği bağımsız bir olay (event) olarak kaydeder. Her olay sistemde gerçekleşen bir işlemi veya değişikliği temsil eder ve olaylar kronolojik sırayla kaydedilir. Bu olaylar daha sonra verinin o andaki durumunu yeniden oluşturmak için kullanılabilir. Böylece sistemin belirli bir zamandaki durumunu yeniden oluşturmak sadece ilgili olayları yeniden işlemekle mümkündür. Bu yaklaşım verinin değişim sürecini tam olarak izlemeyi sağlar ve geçmişe dönük analizler yapmak, hata ayıklamak veya belirli bir zamana geri dönmek için büyük avantajlar sunar. Ayrıca event sourcing, mikro hizmet mimarileri ve dağıtık sistemler için de uyumlu bir model olup, ölçeklenebilirlik ve esneklik açısından da önemli faydalar sağlar.
Bu iki yaklaşım arasındaki temel fark, geleneksel veri yönetiminin mevcut durumu korurken, event sourcing’in verinin evrimini ve değişim sürecini korumasıdır. Geleneksel modelde, anlık durumu kaydetmek ön plandayken, event sourcing’de her değişiklik adım adım kaydedilir ve bu sayede verinin tüm geçmişi detaylı bir şekilde izlenebilir.
Golang ve Redis ile Event Sourcing Örneği
Golang ve Redis kullanarak basit bir event sourcing uygulaması yapalım. Bu örnekte bir sipariş yönetim sistemi oluşturacağız. Kullanıcılar sipariş verecek ve bu siparişler olaylar olarak kaydedilecek.
Adım 1: Proje Kurulumu
Öncelikle, gerekli bağımlılıkları yükleyelim:
go mod init event-sourcing-example
go get github.com/go-redis/redis/v8
Adım 2: Redis Bağlantısı
package main
import (
"context"
"github.com/go-redis/redis/v8"
"log"
)
var ctx = context.Background()
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
_, err := rdb.Ping(ctx).Result()
if err != nil {
log.Fatalf("Could not connect to Redis: %v", err)
}
log.Println("Connected to Redis")
}
Adım 3: Event Yapısı
Olayları temsil eden bir yapı tanımlayalım:
package main
import "time"
type Event struct {
EventType string
Data interface{}
Timestamp time.Time
}
type OrderCreated struct {
OrderID string
UserID string
Amount float64
}
Adım 4: Olay Kaydetme ve Yükleme
Olayları Redis’e kaydedecek ve yükleyecek fonksiyonları yazalım:
package main
import (
"encoding/json"
"fmt"
"github.com/go-redis/redis/v8"
"log"
"time"
)
func SaveEvent(rdb *redis.Client, event Event) error {
key := fmt.Sprintf("events:%s", event.EventType)
data, err := json.Marshal(event)
if err != nil {
return err
}
return rdb.RPush(ctx, key, data).Err()
}
func LoadEvents(rdb *redis.Client, eventType string) ([]Event, error) {
key := fmt.Sprintf("events:%s", eventType)
data, err := rdb.LRange(ctx, key, 0, -1).Result()
if err != nil {
return nil, err
}
var events []Event
for _, d := range data {
var event Event
if err := json.Unmarshal([]byte(d), &event); err != nil {
return nil, err
}
events = append(events, event)
}
return events, nil
}
Adım 5: Komut ve Projeksiyonlar
Sipariş oluşturma komutunu ve projeksiyonu yazalım:
package main
import (
"log"
"time"
)
func CreateOrder(rdb *redis.Client, orderID, userID string, amount float64) error {
event := Event{
EventType: "OrderCreated",
Data: OrderCreated{
OrderID: orderID,
UserID: userID,
Amount: amount,
},
Timestamp: time.Now(),
}
return SaveEvent(rdb, event)
}
func GetOrderSummary(rdb *redis.Client) {
events, err := LoadEvents(rdb, "OrderCreated")
if err != nil {
log.Fatalf("Could not load events: %v", err)
}
for _, event := range events {
order := event.Data.(map[string]interface{})
log.Printf("Order ID: %s, User ID: %s, Amount: %v", order["OrderID"], order["UserID"], order["Amount"])
}
}
Adım 6: Uygulamayı Çalıştırma
Son olarak, ana fonksiyonda sipariş oluşturup özetini alalım:
package main
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
_, err := rdb.Ping(ctx).Result()
if err != nil {
log.Fatalf("Could not connect to Redis: %v", err)
}
CreateOrder(rdb, "1", "user1", 99.99)
CreateOrder(rdb, "2", "user2", 49.99)
GetOrderSummary(rdb)
}
Bu kod, Redis’e bağlanarak iki sipariş oluşturur ve ardından bu siparişlerin özetini ekrana yazdırır.
Program çalıştırıldığında çıktısı aşağıdaki gibi olacaktır.
Order ID: 1, User ID: user1, Amount: 99.99
Order ID: 2, User ID: user2, Amount: 49.99
Event sourcing, veri yönetimi ve sistem tasarımında güçlü bir desendir. Golang ve Redis kullanarak basit bir event sourcing uygulaması oluşturduk. Bu yaklaşımın avantajlarını, olayların nasıl saklandığını ve yüklendiğini gördük. Event sourcing ile ilgili daha ileri konular arasında olay işleme, dağıtık sistemler ve mikro servis mimarileri bulunmaktadır. Bu temel örnek, event sourcing’i anlamanıza ve uygulamanıza yardımcı olacaktır.
Kaynaklar
- https://microservices.io/patterns/data/event-sourcing.html
- https://www.martinfowler.com/eaaDev/EventSourcing.html
- https://www.linkedin.com/pulse/event-sourcing-pattern-distributed-designpatterns-pratik-pandey/