"Enter"a basıp içeriğe geçin

Event Sourcing Neydi?

Tahmini okuma süresi: 15 dakika

Önceden servislerimiz aşağıdaki gibi olurdu.

Application Services 01

Bu servisimiz yüzlerce satır koddan oluşurdu. Bu karmaşadan kurtulmak için CQRS ile servislerimizi “Command” ve “Query” olmak üzere ikiye böldük. CRUD işlemlerimizi “Command” servisleri ile, sorgulama işlemlerimizi “Query” servisleri ile yaptık. Ortaya aşağıdaki gibi iki ayrı servis çıktı.

Application Services 02

Bu şekilde az da olsa okunabilirlik sağlamış olduk ama halen god class‘lar ortaya çıkıyordu. Mediator ile birlikte “UseCase”ler hayatımıza girdi. Bu sefer her bir “UseCase” için ayrı handler‘lar oluşturmaya başladık. Ortaya aşağıdaki gibi 13 ayrı handler çıktı.

Application Services 03

Bu yaklaşım ile her bir handler sadece kendi işini yapar oldu. Kod satırları daha da azaldı, okunabilirlik daha da arttı. Bu da bize kolay yönetilebilirlik sağladı.

Bu sefer performans üzerinde çalıştık. Ortaya çıkan her veritabanı teknolojisi farklı özellikler sunuyordu. Kimisinin yazma hızı çok iyiydi, kimisinin okuma hızı. Madem her şeyi böldük… “Command” işlemleri için yazma hızı iyi veritabanı teknolojilerini, “Query” işlemleri için okuma hızı iyi veritabanı teknolojilerini seçtik. Ortada bir sorun vardı. İki farklı veritabanı teknolojisindeki verileri senkronize etmemiz gerekiyordu.

Bu noktada “Messaging” teknolojileri imdadımıza yetişti. Command handler‘da verileri veritabanına yazdıktan sonra aynı verileri kuyruklara mesaj olarak gönderdik. O kuyrukları dinleyen consumer‘lar yazdık. Consumer‘lar ile gelen mesajları “Query” işlemleri için seçtiğimiz veritabanına aktardık. Bu şekilde iki farklı veritabanı teknolojileri arasında senkronizasyon sağladık.

Daha sonra her şeyin CRUD olmadığını anladık. Her şey veriyi eklemek, güncellemek ve silmek değildi. Neyi güncelliyorduk?

Meetup uygulamasını düşünelim.

Örneğin bir etkinlik tanımlıyoruz. Bu bir insert‘dir.

Etkinliği tanımladıktan sonra etkinlik içeriğini güncelleyebiliyoruz. Bu bir update‘dir.

Etkinliğe katılmayı düşünenler etkinliğe gideceklerini belirtiyor. Bu etkinlik için update midir? Update değilse nedir?

Etkinlik başlamadan 1 saat önce etkinliğe kayıt alınmaması için etkinliğin durumunu “Stopped” olarak işaretliyoruz. Peki bu bir update midir?

Etkinlik sona erdikten sonra etkinliğin durumunu “Completed” olarak işaretliyoruz. Peki bu update midir?

Etkinlik sahibi olarak etkinliğe fotoğraflar yüklüyoruz. Bu da mı update‘dir?

Etkinliğe katılanlar etkinliğe yorum giriyorlar. Update mi sizce?

Gördüğünüz gibi her şey Insert, Update ve Delete olarak düşünülemezdi. Aslında her şey aşağıdakiler gibi bir event‘ti.

  • MeetupRegistered
  • MeetupJoined
  • MeetupStopped
  • MeetupCompleted
  • MeetupAddedPhoto
  • MeetupAddedComment

Her şey bir event ise verilerimizi klasik yöntemdeki gibi ilişkili tablolarda satırlar halinde tutmaya gerek yoktu. O zaman aşağıdaki görseldeki gibi bir tane tablo oluşturup her event‘i bu tabloya bir satır olarak ekledik.

Events Table

Baktığımızda tanımladığımız etkinlik bu satırların birleşiminden ibaret. Her bir event‘in versiyon numarası var. Aşağıdan yukarıya doğru birleştirerek çıktığımızda aşağıdaki gibi bir sonuç ortaya çıkıyor.

Tanımladığımız etkinlik; bilgileriyle, fotoğraflarıyla, yorumlarıyla bir bütün olarak “Aggregate”i temsil eder. Bu sebeple AggregateId bilgisi bulunuyor.

“Command” işlemlerinde verilerimizi yukarıdaki gibi event olarak bir veritabanına kaydediyoruz dedik. Fakat bu satırlarda sorgulama yapamayacağımız için bu event‘leri aynı zamanda “Query” için seçtiğimiz veritabanına da yazmamız gerekli. Örneğin; meetup uygulamasında “tamamlanmamış etkinlikleri” göstermek istediğimiz zaman yukarıdaki event‘lerde bir sorgulama yapamayız.

Önceki paragraflarda bahsettiğim gibi etkinlik bilgilerini “Command” işlemlerinde veritabanına event olarak yazdıktan sonra bu bilgileri bir “messaging” kuyruğuna göndermeliyiz. Bu kuyruğu dinleyen bir consumer yazmalıyız. Bu consumer aracılığıyla “MeetupRegistered” event‘lerini dinleyip gerekli bilgiler ile “Query” veritabanına etkinlik bilgilerini yazmalıyız. Yine aynı şekilde “MeetupCompleted” event‘lerini de dinleyip “Query” veritabanından eklinlik bilgilerini silmeliyiz.

Event Sourcing Diagram

Yukarıda da gördüğünüz gibi “Command” bölgesinde API’den gelen event‘ler event store‘a yazılır. Bu event‘ler aynı zamanda kuyruğa gönderilir. “Projection” bölgesinde kuyruktaki mesajlar işlenerek query store‘a yazılır. “Query” bölgesinde sorgulama işlemleri yapılarak API’den sonuç döndürülür.

Event Sourcing dediğimiz şey tam olarak budur. Aggregate, Projection, Snapshot vb. gibi kendine has terimleri var. Peki “Command” için, “Messaging ” için ve “Query” için seçeceğimiz teknolojiler ne olabilir?

Event‘ler için “MSSQL” kullanılabilir. “Messaging” için “RabbitMQ” kullanılabilir. “Query” için ise “RavenDB, ElasticSearch, Couchbase” gibi teknolojiler kullanılabilir. Fakat Event Sourcing için .NET dünyasında “Event Store” adında bir teknoloji var. Bu teknoloji “Aggregate” ve “Projection” için çözüm sunmaktadır. Yani event‘leri kaydedebileceğimiz store‘u sağlamakla birlikte “Query” veritabanlarına kayıt atabilmemiz için gerekli olan “Messaging” ve “Projection” hizmetini de sağlamaktadır.

Blog’umda teorik bilgilerden çok pratiğe yer veriyorum. Yani Tutorial tarzı yazılar yazmayı tercih ediyorum. Fakat Event Sourcing için öncesinden teorik bilgi aktarmak istedim. Yakın zamanda teoriyi pratiğe dökeceğimiz bir video ile karşınıza gelmek niyetindeyim. O video’da uzun uzadıya şu nedir bu nedir gibi konulara girmeden örnek bir uygulama geliştireceğim. Bu sebeple bu teorik yazı ile video’ya temel hazırlamak istedim.

Kolay gelsin.

4 Yorum

  1. Süleyman Engin Süleyman Engin

    Yazı için teşekkürler. Devamını bekliyorum.

  2. Tufan Tufan

    Gerçekten mükemmel tek kalemde soluksuz okudum. Bahsettiğin video ne zaman gelecek?

    • ahmetkucukoglu ahmetkucukoglu

      Merhaba,

      Teknik sebeplerden dolayı video konusunda henüz sonuç alamadım. Sözüm yerde kalmasın diye de videoda anlatacaklarımı seri yazı halinde yazmak istedim. Şimdilik iki seri yazıyı yazdım. Devamı gelecek.

      İlginiz için teşekkür ederim.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir