Bir Olayın İzini Sürmek: Event-Sourcing ile Derinlemesine Yolculuk

Tracing an Event: Journey Deeper with Event-Sourcing

Cihat Solak
Intertech
11 min readJun 1, 2024

--

Event Sourcing, mikroservis mimarilerinde sıkça kullanılan bir tasarım desenidir. Verileri append-only (yani sadece ekleme yapılabilir) bir şekilde olaylar halinde kaydeden bir yapıdır. Her yapılan değişiklik bir olay olarak temsil edilir ve bu olaylar event log’a kaydedilir. Event log, olayların kaydedildiği veri tabanını ifade eder. Bu kaydedilen olaylar oynatılarak bir varlığın güncel durumu elde edilir. Finans, lojistik, sağlık, perakende gibi birçok alanda kullanılmaktadır.

Tracing an Event: Journey Deeper with Event-Sourcing
Tracing an Event: Journey Deeper with Event-Sourcing

Append-only” terimi, bir veri setine sadece ekleme izni veren bir yaklaşımı ifade eder. Varolan verileri değiştirmek (update) veya silmek (delete) mümkün değildir, yalnızca yeni veri eklemek (insert) mümkündür.

Event Sourcing Avantajları

Veri tabanı herhangi bir nedenden dolayı arızalansa veya çökse bile, event’lerin yeniden oynatılma (re-play) yöntemiyle varlıkların güncel durumu elde edilebilir.

Tüm state değişiklikleri, örneğin bir ürünün adının değişmesi veya eklenmesi gibi, event-sourcing kullanılarak veri tabanına bir olay olarak kaydedilir. Bu, debugging (hata ayıklama) süreçlerinde önemli bir iz bırakır. Event-sourcing’in loglamadan ayıran önemli bir özellik, veri tabanı çöktüğünde event’lerin yeniden oynatılarak varlığın güncel durumunun elde edilebilmesidir. Yani, event’ler farklı bir veri tabanında kaydedilir ve bu event’lerden oluşan güncel veri, ayrı bir veri tabanında tutulur. Veri tabanı çöktüğünde event’ler tekrar oynatılarak güncel durum elde edilebilir. Bu, loglama işlemleriyle gerçekleştirilemeyen bir süreci daha etkili bir şekilde sağlar.

Audit, event-sourced sistem, tüm event’ları değiştirilemez (immutable) bir dizi halinde kaydeder.

Event-sourcing kullanıldığında, ekstra loglama yapma ihtiyacı ortaya çıkmaz, çünkü event’ler zaten bir tür log görevi görür. Event’lerin önemli bir özelliği ise değiştirilemez olmalarıdır; bu da verilerin güvenilir ve izlenebilir olmasını sağlar.

Bütün state (durum) değişiklikleri kaydedilir ve herhangi bir zaman diliminde geri dönülebilir.

CQRS and Event Sourcing are complementary architectural patterns with different focuses; while CQRS emphasizes command query responsibility segregation, Event Sourcing manages system state by recording data changes as events.
CQRS and Event Sourcing are complementary architectural patterns with different focuses; while CQRS emphasizes command query responsibility segregation, Event Sourcing manages system state by recording data changes as events.

Eventler, geçmiş zamanı temsil eder (BankNameChanged, CustomerUpdated) ve her bir event, unique bir metadata (timestamp, identifier) içerir. Bu metadata, her event’ı diğerlerinden ayırt etmek için zaman damgası ve kimlik numarasını içerir. Bu sayede, her event farklı bir işlemi temsil eder ve kronolojik sırayla kaydedilir. Yeni eklenen her event, önceki event’ın sonuna eklenir ve araya kesinlikle eklenmez.

Senaryo: Santraç ♟♛♞

Bir satranç uygulaması geliştiriyorsanız ve kullanıcı oyun oynarken belirli adımlar atıyorsa, örneğin A7'deki piyonu A5'e çekiyorsa, bu adımları kaydetmek önemlidir. Çünkü kullanıcı oyunu kaydettiğinde ve daha sonra tekrar açtığında, kaldığı yerden devam etmek isteyecektir. Her adımı kaydetmek, sadece en son durumu değil, baştan beri yapılan tüm hamleleri içerir. Bu, kullanıcıya ileri geri gitme esnekliği sağlar. Örneğin, 5. hamleden itibaren devam etmek veya 10. hamleden itibaren devam etmek mümkün olur.

Event Sourcing records each move in a chess game as an event, reconstructing the entire history of the chess game and determining its current state.
Event Sourcing records each move in a chess game as an event, reconstructing the entire history of the chess game and determining its current state.

Eğer sadece en son durumu kaydederseniz, kullanıcıya animasyonlu bir şekilde oyunun her adımını gösterme veya belirli bir adıma geri dönme imkanı veremezsiniz. Ayrıca, kullanıcı önceki bir pozisyona dönmek istediğinde bu mümkün olmaz. Bu, kullanıcıyı sınırlar ve esneklikten yoksun bırakır.

Ancak, event sourcing yaklaşımıyla her bir adımı kaydetmek, gerçek senaryolarda bu tür esneklikleri sağlar. Kullanıcı oyunu bitirdiğinde, her adımı animasyonlarla gösterme şansınız olur. Bu, kullanıcıya oyunun gelişimini keyifli bir şekilde takip etme imkanı sunar. Event sourcing burada kullanıcının oyun deneyimini genişleten ve esneklik sağlayan bir yaklaşımdır.

Senaryo: Sağlık 🧘‍♀️🥗🍎🌱🔆

Doktorlar, engelli vatandaşlara verilen raporları oluştururken çeşitli hatalar yapabilir ve bu veriler kritik öneme sahip olabilir. Örneğin, yanlış bir rapor oluşturulabilir, yanlışlıkla silinebilir veya üzerinde yanlış değişiklikler yapılabilir. Bu tür durumlarda, her adımın kaydedilmesi ve bu verilerin adım adım izlenebilmesi önemlidir.

In the healthcare sector, Event Sourcing ensures access to historical medical data and reliable traceability by recording each interaction, such as the creation and updating of doctor reports and patient records, as an event.
In the healthcare sector, Event Sourcing ensures access to historical medical data and reliable traceability by recording each interaction, such as the creation and updating of doctor reports and patient records, as an event.

Event Sourcing, doktorun her adımını adım adım veri tabanına kaydederek bu sorunu çözebilir. Rapor oluşturma, değiştirme, silme veya herhangi bir değişiklik, sistem tarafından adım adım kaydedilir. Bu, ileride hukuki bir durum ortaya çıktığında, her bir değişikliğin ne zaman, neden ve kim tarafından yapıldığını kolayca takip etme imkanı sağlar.

Eğer Event Sourcing kullanılmazsa, sadece en son durum kaydedilir ve geçmiş veriler kaybolabilir. Örneğin, bir hasta ismi değiştirildiğinde, önceki ismi bilgi olarak kaybolur. Bu durumda, loglama kullanılabilir ancak Event Sourcing’in avantajı, hem loglama işlemini hem de veri tabanına kaydetme işlemini bir araya getirerek daha etkili bir çözüm sunmasıdır. Event Sourcing sayesinde her adım otomatik olarak bir event log’a dönüşür, bu da hem kaydetme hem de izleme işlemlerini kolaylaştırır.

Senaryo: E-Ticaret 🛒

Sepete ürün ekleyip silme gibi kullanıcı işlemlerini event-sourcing kullanarak veri tabanında kaydetmek, kullanıcı davranışlarından anlamlı veriler çıkartmayı mümkün kılar.

In e-commerce systems, Event Sourcing enables tracking and managing orders, inventory, and customer behaviors by recording each customer interaction as an event
In e-commerce systems, Event Sourcing enables tracking and managing orders, inventory, and customer behaviors by recording each customer interaction as an event

Örneğin, bu yöntemle bir ay içinde kaç kullanıcının ürün eklediğini veya sildiğini tespit etmek mümkün olur. Eğer bu adımlar kaydedilmezse sadece son durum görüntülenebilir ve kullanıcı davranışlarından anlamlı veriler çıkartma konusunda kısıtlamalar getirilebilir. Bu durum özellikle e-ticaret sepet uygulamalarında yaygın olarak kullanılan bir örnektir.

Sağlık veya finans uygulamalarında, kritik verilerin işlendiği yerlerde, rapor oluşturma süreçleri veya kritik veri değişiklikleri, event-sourcing kullanılarak her adımın veri tabanına kaydedilmesiyle güvence altına alınır. Bu yöntem, hukuki durumları netleştirmek ve olası problemlere karşı hazırlıklı olmak adına büyük kolaylık sağlar. Event-sourcing, bir verinin geçmişini takip etmek ve A noktasından B noktasına geldiğinde o verinin yaşadığı her olayı görmek istendiğinde önemli bir avantaj sunar. Bu bağlamda, kullanıcının her adımını veri tabanında bir event olarak kaydetme stratejisi, verilerin güvenli ve izlenebilir bir şekilde yönetilmesini sağlar.

Verilerin geçmişini görmek istiyorsak ve bir noktadan diğerine nasıl ulaşıldığını anlamak önemliyse, event-sourcing kullanmak gereklidir.

CQRS — Command Query Responsibility Segregation

Event-sourcing ve CQRS pattern’larını bir arada kullanmak, birbirini tamamlayan iki uyumlu deseni ifade eder. CQRS, tek başına kullanılabilir, ancak event-sourcing’in olduğu bir ortamda CQRS’in varlığı büyük bir kolaylık sağlar.

CQRS and Event Sourcing are complementary architectural patterns with different focuses; while CQRS emphasizes command query responsibility segregation, Event Sourcing manages system state by recording data changes as events.
CQRS and Event Sourcing are complementary architectural patterns with different focuses; while CQRS emphasizes command query responsibility segregation, Event Sourcing manages system state by recording data changes as events.

Command Query Responsibility Segregation (CQRS), komutların ve sorguların ayrılması prensibini temsil eder. Bu prensipte, bir UI tarafından gerçekleştirilen işlemler iki kategoriye ayrılır. Birincisi “command” olarak adlandırılanlar, veri tabanının durumunu değiştiren eylemlerdir, yani güncelleme, silme ve ekleme gibi işlemler. Bu işlemler tamamen bir “command” servisi tarafından gerçekleştirilir. Diğer kategori ise “query” olarak adlandırılan, veri tabanından okuma işlemleridir. Bu işlemler ise ayrı bir servis tarafından ve farklı bir iş mantığıyla gerçekleştirilir.

Soru: Read ve Insert/Update/Delete işlemleri birbirinden ayrıldığında nasıl bir avantaj elde edilir?

CQRS, command’lar (insert/update/delete) ve query’ler arasında geri dönüş tipleri farklı olduğu için bu işlemleri birbirinden ayırmayı ve farklı veri tabanlarına kaydetmeyi önerir. Insert/update/delete işlemlerinde genellikle sadece başarılı olma durumunu bekleriz, bu nedenle geri dönüş tipleri query’lerden farklıdır. Yazma ve okuma veri tabanlarını birbirinden ayırmak için asenkron mesajlaşma ve bir mesaj broker kullanma yöntemi önerilir. Örneğin, bir product entity’sinin kaydedilmesi durumunda, yazma veri tabanı olarak MongoDB kullanılırken, aynı veri RabbitMQ üzerinden dinlenerek okuma veri tabanı güncellenir. Bu ayrım, performans artışına olanak tanır ve okuma/yazma işlemlerini farklı veri tabanlarına yönlendirmek CQRS’in temel amacıdır. Başlangıçta tek bir veri tabanı kullanılarak ileride kolayca ayrılabilir veya aynı veri tabanı kullanılarak başlanıp CQRS’in sağladığı esneklikle ilerleyen zamanlarda ayrılabilir. Bu ayrımın senkronizasyonunu sağlamak için ise message broker ve asenkron mesajlaşma kullanılır. CQRS, command’ları ve query’leri mantıksal olarak birbirinden ayırmak ve veri tabanlarını etkili bir şekilde yönetmek için birçok avantaj sunar.

CQRS, özellikle okuma tarafında karmaşık join’lerin kullanıldığı durumlarda SQL Server gibi ilişkisel veri tabanlarında view’ler oluşturarak performans artışı sağlar. Bu view’ler, birden fazla tablodan oluşan yapılardır ve direkt olarak UI’nin istediği veriyi içerir. Query’ler view’leri kullanarak doğrudan istenilen veriyi döndürür, bu sayede her istek geldiğinde ayrıntılı join işlemleri yapma ihtiyacı ortadan kalkar. CQRS, command ve query işlemlerini birbirinden ayırarak event-sourcing ile mükemmel bir uyum sağlar, bu iki pattern birbirine tam anlamıyla uyumlu bir yapı oluşturur.

Genel Konsept

Bu durumda, iki farklı veri tabanı bulunmaktadır. Birincisi, eventleri kaydedildiği veri tabanıdır; diğeri ise bu eventlerden elde edilen verilerin güncel halinin oluşturulduğu veri tabanıdır. CQRS bu yapıya uygun bir şekilde çalışır. Örneğin, bir ürün varlığının oluşturulması, adının değiştirilmesi veya fiyatının değiştirilmesi gibi olaylarla ilgili eventler üretilir. Bu eventler bir event store’a gönderilir, bu event store herhangi bir veri tabanı olabilir, örneğin SQL Server veya NoSQL MongoDB. Bu eventler, veri tabanında her biri bir satır olarak kaydedilir, bu da command’e karşılık gelir. Çünkü her event, state’de bir değişiklik anlamına gelir.

Event Store is a specialized database used in Event Sourcing architecture to store events, preserving the historical state of the system.
Event Store is a specialized database used in Event Sourcing architecture to store events, preserving the historical state of the system.

Bu eventlerin ardından her bir event, belirlenen farklı bir veri tabanında ilgili verinin güncel hali tutulur. Bu, bir okuma tablosu değil, bir eventleri kaydetme tablosudur. Her event kaydedildiğinde, ilgili event message broker’a gönderilir. Message broker’ı dinleyen consumer, her bir event’i veritabanına bir satır olarak yansıtır.Örneğin, ProductCreated eventi geldiğinde, bu event kaydedilir, mesaj broker’ına gönderilir, broker’ı dinleyen consumer gidip ProductCreated eventiyle ilgili bir ürün nesnesi oluşturur. Aynı şekilde ProductNameChanged eventi geldiğinde, bu event kaydedilir, mesaj broker’ına gönderilir, işlem gider ve ilgili ürünün adını değiştirir.

Bu şekilde, en güncel veriye sahip olmak için bir veri tabanı (örneğin SQL Server) ve eventlerin geçmişini tutmak için bir event store kullanılabilir. Kullanıcı ürünlerini listelemek istediğinde, sorgular (queries) SQL Server veritabanına yönlendirilir. Bu şekilde, CQRS ve event-sourcing birbirine mükemmel bir şekilde uyum sağlar.

Event Database

Eventlerin tutulacağı veri tabanının seçimi konusunda esneklik mevcuttur. SQL veya NoSQL tipini tercih edebilirsiniz. Eventler, kronolojik bir sıra içinde alt alta kaydedilir ve kesinlikle araya event eklenmez; her yeni event bir öncekine eklenir. Bu kurala dikkat etmek önemlidir.

Event sourcing mimarisinde 2 adet veri tabanına ihtiyaç duyulur.
Birincisi, eventlerin güncel halini tuttuğunuz veritabanı, diğeri ise CQRS’nin sağladığı şekilde query’leri ve command’ları birbirinden ayırdığınız veritabanıdır. Bu durum, event sourcing ve CQRS’nin birebir uyuştuğunu gösteriyor.

Stream — Tablo

Event sourcing içinde kullanılan stream kavramı, özel bir domaine özgü olan eventlerin belirli bir sıra içinde kaydedilmesini ifade eder. Her bir stream, bir domain objesi için geçmişi tutan ve değişiklik tarihini içeren bir kaynaktır. Örneğin, product stream bir ürünle ilgili geçmişteki eventleri içerir ve bu eventlerin kaydedildiği özel bir veritabanı stream’idir.

Stream kavramı, event store içinde eventleri düzenleyen ve kaydeden bir yapıyı temsil eder. Bir stream, adını verdiğiniz domain objesinin geçmişini temsil eder; örneğin, card stream sepet uygulamasıyla ilgili geçmişi içerir.

Event streamler, domain objeleri için bir kaynak olarak hizmet eder ve değişiklik tarihini saklar. Eventleri stream’den okuyarak güncel state elde edilebilir. Önemli olan, asıl verinin event store içinde saklanmasıdır. Stream’ler, birbirinden ayıran unique değerlere sahiptir, her bir stream birbirinden farklıdır ve event’ler artan bir pozisyon id’si ile belirli bir sıraya göre kaydedilir.

EventStoreDB

EventStoreDB, event sourcing mimarisi için özel olarak geliştirilmiş bir veri tabanıdır. Bu veri tabanı, depoladığı eventleri kronolojik bir sırada muhafaza eder ve kullanıcılara bu eventleri dinleme yeteneği sağlar. İçerisinde bir notification sistemi barındırarak kaydedilen eventleri dinlemek ve bu değişikliklere tepki vermek için olanak tanır. Açık kaynaklı bir yapıya sahip olan bu veri tabanı, HTTP ve TCP protokollerini kullanarak iletişim kurabilir, bu da çeşitli platformlarla entegrasyonu kolaylaştırır.

Event Store is a specialized database used in Event Sourcing architecture to store events, preserving the historical state of the system.
Event Store is a specialized database used in Event Sourcing architecture to store events, preserving the historical state of the system.

EventStoreDB, event sourcing prensiplerine uygun bir şekilde çalışarak sistemlerin olaya dayalı mimarilerini etkin bir şekilde destekler.

Projection (İzdüşüm/Yansıtma)

Bir stream içindeki eventlerin belli bir tarih aralığındaki en güncel halini almayı sağlayan kavramdır. Örneğin, bir yıl boyunca kaydedilen eventlerden, belirli bir tarih aralığındaki en güncel durumu elde etmek için bu stream üzerinden bir projection alabilirsiniz. Bu, örneğin 6. aydan itibaren 8. aya kadar olan en güncel durumu veya ayın 30'unda en güncel datayı görmek için kullanılabilir.

Event Store Projection is a mechanism that processes events stored in Event Store to be utilized for a specific purpose.
Event Store Projection is a mechanism that processes events stored in Event Store to be utilized for a specific purpose.

Subscription (Abonelik)

Event store’un içerisinde bulunan bir bildirim sistemidir. Bir stream’e abone olduğunuzda, event store her bir event kaydedildikten sonra abone olduğunuz servise bu event’i ileter. Örneğin worker servis aracılığıyle read veri tabanını güncelliyorsanız, ilgili worker servisin read database’ini güncellemesini sağlar. Bu abonelik sistemi olmadığında, eventleri kaydettiğiniz veri tabanını güncellemek için bir message broker kullanmanız gerekebilir, ki bu ek bir iş yükü getirir. Hem event store hem de message broker genellikle open source olarak bulunabilir. Özel olarak tasarlanmış bir veri tabanı olan event store’u kullanmak, ilişkisel veri tabanlarından kaçınmak için daha mantıklıdır, çünkü eventleri kaydetmek için genellikle ilişkisel veri tabanları uygun değildir.

Event Store Subscription is a mechanism that listens to and processes events of a specific type, used to monitor changes in the system and react accordingly.
Event Store Subscription is a mechanism that listens to and processes events of a specific type, used to monitor changes in the system and react accordingly.

Volatile Subscription (Uçucu Abonelik), geçici ve uçucu bir abonelik tipidir. Bu tip abonelikte, belirli bir noktadan sonraki yeni event’leri dinleyebilirsiniz. Ancak bu yöntem, bağlantı koparsa veya tekrar bağlandığınızda araya eklenen event’leri kaçırmanıza neden olabilir. ☠️ Yani, bağlandığınız an itibariyle gelen event’leri takip edersiniz ve bağlantı koparsa sonrasında eklenen event’leri göremezsiniz. Bu durum, read data modeli oluştururken uygun bir senaryo değildir.

Catch-up Subscription (Telafi Aboneliği), stream’e abone olduğunuzda event’leri sıfırdan itibaren dinlemeye başlar. Örneğin, 1000 event varsa, bu event’leri tek tek dinleyebilir ve ardından 1000 event tamamlandıktan sonra yeni eklenen event’leri de alabilirsiniz. Ancak bağlantı koparsa, tekrar bağlandığınızda sıfırdan dinlemeye başlarsınız. Bu nedenle, catch-up subscription kullanıyorsanız dinlediğiniz en son event’i kaydetmeniz gerekir. ❗❗ Örneğin, 500. eventi dinlediyseniz, bağlantı koparsa tekrar bağlandığınızda en son hangi event’i dinlediğinizi bilirsiniz.

Persistent Subscription (Kalıcı Abonelik), tercih edilen modeldir. Bu modelde, hangi event’te kaldığınızı siz değil, event store takip eder. Örneğin, 1000 event içeren bir stream üzerinde 1'den başlayarak dinlemeye başladığınızı düşünelim. Eğer 100. event’e kadar dinledikten sonra process’iniz çökerse, tekrar bağlandığınızda event store, kaldığınız 101. event’ten itibaren size yeniden event göndermeye başlar. Eğer 101. event’i alırken sistem çökerse, tekrar bağlandığınızda event store, 101. event’ten devam eder. Yani event store, hangi event’i göndermediğini bilir ve sizin kaldığınız yerden itibaren eksik event’leri size tekrar iletir. Bu, çökmeler durumunda daha güvenilir bir yöntemdir. Persistent Subscription, diğer abonelik türlerinden farklı olarak, kullanıcının hangi event’i dinlediğini takip etme yükünden sizi kurtarır. Ancak catch-up subscription da kullanılabilir. Önemli olan, bir abone olarak hangi event’i dinleyeceğinizi tek tek takip etmek yerine event store’un bu bilgiyi tutmasına izin vermek ve persistent subscription kullanmaktır.

New Subscription (Yeni Abonelik) Nasıl Oluşturulur?

Group: Read Data modelinde eventleri dinleyip SQL Server’a yazacak birden fazla işlem olabilir. Aynı gruba abone olan birden fazla işlem olduğunda, Event Store bir message broker gibi davranır ve eventleri bu işlemlere sırayla gönderir. Ancak dikkat edilmesi gereken nokta şudur: Event Store, aynı eventi değil, sıralı bir şekilde farklı eventleri gönderir. Yani, bir gruba abone olan her işlem, sırayla farklı bir event alır. Örneğin, birinci işleme birinci eventi, ikinci işleme ikinci eventi gönderir. Bu şekilde, grup oluştururken bu dinamik bizim için önemlidir.

Min Checkpoint Count: Yukarıdaki abonelikte değerinin 1 olarak ayarlamanın amacı şudur: Eğer değer 10 olarak ayarlansaydı, dashboard’da kullanıcıların kaç event dinlediğini doğru bir şekilde görebilmek için en güncel verinin alınmasında zorluk çıkabilirdi. Her 10 event gönderildiğinde dashboard’un güncellenmesi durumunda, dashboard bir güncelleme alır. Ancak bu durumda, yanlış veri görülebilir. Bu nedenle, bu değer 1 olarak belirlendi. Bu ne anlama geliyor? Minimum 1 event gönderildiğinde checkpoint olarak, dashboard’un güncellenmesini sağlanıyor.

Event Sourcing RePlay

Event Sourcing mimarisindeki önemli bir özelliktir. Bu özellik, veri tabanı veya uygulama durumu gibi bir sistem unsuru çöktüğünde, kaydedilmiş olan olayların yeniden oynatılmasını sağlar.

Event Sourcing RePlay is a mechanism that reconstructs the past of events in the system to restore its state.

Yani, sistem çökmesi durumunda, mevcut olayların kayıtları tutulduğu için, bu olaylar tekrar oynatılarak sistemin eski durumuna geri dönülmesi veya bir hatanın nedeninin anlaşılması mümkün olur. Bu özellik sayesinde sistemlerde oluşan hataların izlenmesi ve giderilmesi süreçleri daha etkin ve güvenilir bir şekilde gerçekleştirilebilir.

Write Model

CQRS içerisinde command’ların nasıl ele alınacağı ile ilgilidir. Bu comamnd’lar nereye ve nasıl yazılacak?

DDD içerisinde eventleri veri tabanına kaydetme işlemini aggregate’ler içerisinden gerçekleştirmek en uygun senaryodur.

Aggregate içerisinde başarılı yapılan her bir durum yeni bir event olarak ortaya çıkacaktır. Event meydana geldiği zaman bu event, EventStoreDB’ye kaydedilecektir.

Read Model (Stream = Tablo)

CQRS içerisinde query’lerin nasıl ele alanılacağı ile ilgilidir. Business işlemlerinin okunabilir forma dönüşümüdür.

--

--