İlişkisel Veriden Olaylara Geçiş
(event-driven.io)- İlişkisel CRUD modeli depolama yapısını iyi gösterir, ancak iş süreçlerinin üzerinin kolayca örtülmesine yol açar; bu da sistemde gerçekte ne olduğunu izlemeyi zorlaştırır
- Event Sourcing, her işlemin ardından oluşan değiştirilemez olayları bir Event Stream içinde bırakır ve daha sonra karar alma sürecinde mevcut durumu belirlemek için bu liste okunur
- Modelleme, önce olayları bulup ardından kullanıcının davranış niyetini ifade eden komutları (command) ve iş kurallarını bağlayarak süreci anlama akışıyla ilerler
- Mevcut ilişkisel veride olay adaylarını ararken durum sütunlarına, tarih sütunlarına, nullable olup olmadığına ve 1:N ilişkilere bakılır; ancak yalnızca durum değerlerinden tam geçmişin yeniden kurulabileceğini varsaymak risklidir
- Yalnızca son durumun kaldığı veriyi taşırken geçmiş olayları zorla yeniden kurgulamak yerine, Order Imported gibi açık bir import olayıyla başlamak ve güvenli bir ortamda tekrar tekrar doğrulama yapmak daha gerçekçidir
CRUD verisinden olay merkezli modele bakmak
- İlişkisel veri modeli hangi verilerin saklandığını gösterir, ancak sistem içinde neler yaşandığını ve süreçlerin nasıl etkileştiğini anlamayı zorlaştırır
- Mevcut CRUD yaklaşımı verinin üzerine yazarak önemli iş bilgisinin kaybolmasına neden olabilir
- Event Sourcing, depolama boyutundan çok bilgi kalitesini önceler ve her işlemden sonra ortaya çıkan olguları olay olarak saklar
Event Sourcing’in temel modeli
- Olay, zaten gerçekleşmiş bir şeye dair olgudur ve bir kez kaydedildikten sonra değiştirilemeyen veridir
- Event Stream, tek bir kayıt için meydana gelen her şeyi sırayla içeren listedir
- Geçmiş olaylar değiştirilemez, ancak sona yeni bir olay eklenerek önceki bir hata düzeltilebilir
- Karar alma sırasında olay listesi okunur ve doğrulanır; mevcut durum ile sonraki adım buna göre belirlenir
Süreç modelleme sırası
- Modelleme önce olay keşfiyle başlar
- Ardından komutlar (command) bulunur ve hangi eylemi gerçekleştirme niyetini taşıdığı tanımlanır
- Son olarak iş kuralları düzenlenir
- Olaylar, teknik ekip ile iş tarafının süreci birlikte anlaması için merkez eksen görevi görür
- Alberto Brandolini’nin EventStorming yaklaşımında olduğu gibi olaylar, komutlar ve kurallar birlikte görülerek süreç anlaşılabilir
Mevcut ilişkisel veride olay adaylarını bulmak
-
1. Durum sütunlarına bakmak
statussütununun değerleri, verinin yaşam döngüsündeki aşamaları yansıtabilir- Bir sipariş
initiated,shipped,paidgibi durumlara sahipse bunlar sırasıyla Order Initiated, Order Shipped, Order Paid olay adayları olabilir - Ancak durum değerleri, iş sürecini düzleştiren bir yorum olabilir; bu yüzden eksiksiz oldukları varsayılmamalıdır
- Olay adlarını Order Created, Order Updated, Order Deleted gibi CRUD işlemleri üzerinden vermekten kaçınılmalıdır
- State Obsession kaçınılması gereken bir yaklaşım olarak gösterilir
-
2. Tarih sütunlarını incelemek
- Tarih sütunları, süreç yaşam döngüsünde önemli gerçekleşme anlarını gösterebilir
- CreatedDate ve ModifiedDate çok bilgi vermezken, ShipmentDate, DeliveryDate, OrderPlacementDate daha iyi ipuçları sunar
- Örnekler:
- ShipmentDate, Order Shipped olayının eklenmesi için bir ipucu olabilir
- OrderPlacementDate, Order Initiated yerine Order Placed adının daha iyi olabileceğini düşündürür
- DeliveryDate, Order Delivered olayına ihtiyaç olabileceğini gösterir
- Bu ipuçları, gerçek iş süreciyle örtüşmesi için alan uzmanlarıyla doğrulanmalıdır
-
3. Sütunların nullable olup olmadığını analiz etmek
- non-nullable sütunlar her zaman sağlanması gereken verilerdir
- nullable sütunlar daha sonra başka bir işlemde sağlanan ya da isteğe bağlı değerler olabilir
- Ordering Process içinde zorunlu bir sütunsa, ilgili veri ilk Order Initiated olayına da dahil edilmelidir
- Tek bir olay türü her zaman akışın başlangıç noktası değildir; başlangıç olayı birden fazla olabilir
-
4. 1:N ilişkisi fazla olan tabloları bulmak
- Akış sınırlarını bulmak için önce 1:N ilişkisi çok olan tablolara bakılabilir
- Çok sayıda “one” ilişkisine sahip tablolar, akış türü adayı olabilir
- Verinin birbirinden bağımsız olarak var olup olamayacağı da mantıksal olarak değerlendirilmelidir
- shipment, order’dan ayrı bir süreç olabilir
- order line’ın order olmadan var olması zordur
- Sınırları tartışma süreci, daha fazla olay keşfetmeye ve süreci daha iyi anlamaya yardımcı olabilir
Migrasyon sırasında sahte olaylar üretmemek
- İlişkisel veri düzleşmiş son durum olduğu için, yalnızca bu duruma bakıp gerçekte yaşanmış ayrıntılı geçmiş olayları geriye dönük tahmin etmeye çalışmak başarısız ya da hatalı olabilir
- Geçmişe ait küçük parçalı olayları zorla üretmek yerine, mevcut durumun tamamını ve yorumlama kodunu içeren Order Imported olayını açıkça sağlamak daha iyidir
- Import olayı, verinin sisteme hangi yolla girdiğini net biçimde gösterir ve sorun çözme ile teşhis açısından önemli olabilir
Prototiple doğrulamak
- Migrasyon, güvenli bir ortamda prototip olarak denenmeli ve modelin gerçekte nasıl çalıştığı doğrulanmalıdır
- Sonuçlar beklenen değerlerle karşılaştırılırken yinelemeli biçimde düzeltmeler yapılmalıdır
- Acele etmeden, mevcut bilgiyi kaybetmeden ve bu bilgiyi kullanarak sonraki modeli iyileştiren bir yaklaşım gereklidir
- İlişkisel veriden belge tabanlı yapıya geçişe dair genel strateji, General strategy for migrating relational data to document-based yazısıyla da bağlantılıdır
1 yorum
Hacker News yorumları
2c: Uygulamanın başka bölümlerinde de PostgreSQL gerekiyorsa, event verilerini de PostgreSQL + FOSS raporlama araçlarında (Apache Superset, Metabase vb.) saklayıp kabaca 2TB'a kadar dayanmak iyi olur
Ondan sonra 2TB'ın tamamını çevrim içi tutmanız gerekip gerekmediğine, yoksa gün/saat bazında özetlerin yeterli olup olmadığına karar edebilirsiniz. İkincisiyse PostgreSQL ile devam etmek gayet yeterli olur[1]
Bir müşteri 10TB+ ölçekte saniyede 1.500 event, kayıt başına 600 bayt (indekslemeden önce günde 80GB) işliyor; yalnızca 2 günlük ayrıntılı veriyi çevrim içi tutuyor, geri kalanını özetliyor ve ayrıntıları S3'e taşıyıp Athena SQL ile sorgulamaya devam ediyor[2]
Müşteriye yönelik raporlama portalı dahil toplam maliyet 2 bin doların altında; AWS RDS çoklu AZ otomatik failover (db.m7g.2xlarge) üzerinde hem ekleme hem de raporlama sorguları çalışıyor ve yük %2'nin altında. İş ekibi grafikleri ve çizelgeleri kendisi oluşturduğu için 1 mühendis ayda 5 saatten azını bakıma harcıyor
Tescilli araçlar kullanınca birkaç grafik “hazır” gelebiliyor, ama pgsql kullanınca veri tek yerde oluyor; öğrenilecek sistem tek, çevrim içi tutulacak/çoğaltılacak/yedeklenecek/kurtarılacak sistem tek, güvenliği sağlanacak/ölçeklenecek sistem tek, yönetilecek satıcı tek ve bu sistemi bilen milyonlarca mühendis var
Preset veya Metabase gibi sistemlerde 12 grafik oluşturmak bir saat sürer ve bunu teknik olmayan kişiler de yapabilir
Not: Önyargılıyım, ama 20 yılı aşkın süredir veritabanı ve raporlama sistemlerinin ortaya çıkıp kaybolduğunu gördüm; eski güzel PostgreSQL ise her yıl daha iyi hale geliyor
https://instances.vantage.sh/aws/rds/db.m7g.2xlarge?region=u...
[1] Gerçekten gerekirse ek ölçekleme için PostgreSQL uyumlu sistemler de var. Aurora 3~5 kat, TimescaleDB 10 kat, CitusDB 10 kat+ civarında ölçeklenebilir. Her birinin biraz standart dışına çıkma bedeli var, bu yüzden gerçekten ihtiyaç doğmadan önermem
[2] Müşteri raporlama panosunun 1 saniyenin altında yanıt vermesi gerekiyor; bunu PostgreSQL indekslenmiş özet tabloları sorgulayarak sağlıyor. Athena ise paralel taramayla yaklaşık 1~2 saniyede yanıt veriyor
Kaydetmeden önce verinin snapshot'ını tutup, belirli bir event sırasını tanımlayan/toplayan betikler bulundurup, ardından bir kişinin incelemesiyle yeni mantığın etkisini toplu şekilde geriye dönük uygulamak yeterli olur
https://django-simple-history.readthedocs.io/en/latest/ gibi araçlar audit tabloları oluşturmak için yarı güvenilir ve basit bir çözüm; doğrudan veritabanı erişimini de denetlemek gerekiyorsa Postgres trigger'ları eklenebilir
Teoride event sourcing'i seviyorum, ama pratikte yeni CRUD akışları eklemek veya erken-orta aşama startup'ların beklenmedik durumlarda sıkça yapmak zorunda kaldığı müdahaleleri/hotfix'leri hızlı ve güvenilir biçimde yayımlamak için gereken boilerplate çok fazla
Ödeme işleme rayları uygulamak gibi bir iş değilse event sourcing doğru seçim olmayabilir
https://news.ycombinator.com/item?id=17817375 (2018) içinde de event sourcing'in dezavantajları üzerine iyi bir sohbet var
PostgreSQL'in tek sorunu, ekleme tarafında ilginç ölçeklenebilirlik sorunları olması. Genelde event kaynağı ile DB arasına bir kuyruk koymayı öneririm
{id:uuid,created_at:timestamptz,data:jsonb}tanımına sahip bir tablo kullanmak gibi mi, merak ediyorumÖzellikle event yapıları çeşitliyse ve event tanımları zamanla değişiyorsa JSONB indeks özelliklerinden düzgün yararlanmak zorlaşıyor
Sanırım bu belgeyi daha iyi öğrenmem gerekiyor: https://www.postgresql.org/docs/current/datatype-json.html#J...
Geçmişte ekipte event sourcing’i ciddi şekilde değerlendirdiğimiz olmuştu; bana problem arayan bir çözüm gibi görünmüştü.
Bizde de çalışabilirdi ama faydaları hemen net değildi; yeni bir yaklaşım benimsemenin getireceği riskler ve deneme-yanılma, proje ya da şirket için en iyisi gibi görünmediğinden sonunda vazgeçtik.
Öğrenme fırsatını kaçırmış araç odaklı bir karar da olabilir, ama arkamızdan bir tilki kovalamıyorken o tavşan deliğine girmediğime pişman değilim.
Bu çözümün çözdüğü “problem” tam olarak bu.
Ama çoğu durumda normal bir veritabanı kullanıp geçmiş değişiklik kayıtlarını yardımcı tablolarda tutmak yeterli olur. Böylece ana veritabanı bir tür materialized view gibi davranır.
Büyük bir şikâyetim yok; yanlış bir tercih olduğunu da düşünmüyorum, ama veri modeli değişikliklerini ele alma biçiminde sorun çıkıyor.
Bence çoğu veri saklama yöntemi, günümüzde yazılımın geliştirilme biçimine yetişemedi; event’ler ve queue’lar gibi şeyler de gereken işlevleri mevcut sistemlerin üzerine ekleyerek oluşturmanın sonucu.
Bugün pek çok veri ilişkisi, birden fazla servis arasında, yani veritabanının dışında gerçekleşiyor. Çünkü birçok kuruluşun modern IT ortamı böyle görünüyor.
İşin farklı ekiplerini destekleyen dahili master data var ve işleri basitleştirmek için 300’den fazla IT sistemi ve uygulamasıyla etkileşime giriyor.
Microservices kullanınca iş mantığını ve veri modelini temiz tutmak daha kolay, ama bunun karşılığında event’leri, queue’ları, veri durumunu ve bağımlı depoları da yönetmek gerekiyor; şu anda bu fazlasıyla karmaşık.
SQL’i seviyorum, ama açıkçası bugün kurduğumuz sistemlerin çoğu SQLite’a koyulsa neredeyse yeterli olur gibi geliyor.
Bu tartışmalarda eksik kalan nokta, event-driven architecture’ın ne zaman uygun olduğudur.
Kısaca söylemek gerekirse, müşteri bir şey yaptıysa ve yanıt bekliyorsa bu event-driven değil, basbayağı request/response’tur.
Event-driven yaklaşım, bant dışı bir şey gerçekleştiğinde söz konusudur. Örneğin GitHub’a kod push’layıp bir build’in tetiklenmesi gibi.
Bu örnekte güncellenmiş kodu görmek için sayfayı yenilemek request/response’tur, ama queue’ya alınan CI build’i event-driven’dır.
Umarım yardımcı olur.
Event sourcing veya event-driven yapıda da request-response, inline, blocking, döngüsel akışlar kurabilirsiniz.
Tersine, event sourcing ya da event-driven olmadan da worker, queue, actor, multithreading gibi yöntemlerle asenkron yapıları gayet iyi kurabilirsiniz.
Domain event’leri modellemek, domain uzmanlarıyla çözmeye çalıştığınız problemi anlatmak için faydalıdır; çözümü planlarken dokümantasyonda bırakmak da doğru seviye olabilir.
Uzun süre yaşayan durum makineleri için audit trail sağlayan bir sistemi gerçekten uygulamak istiyorsanız, Temporal.io veya durable functions gibi şeyleri kullanmanız muhtemelen daha iyi olur.
Bu araçlar dahili kalıcılıkta event sourcing kullanır; işlevleri orkestre eden kod (workflows) ile gerçek dünyayla etkileşen koda (activities) farklı kısıtlar ekleyerek sizi yinelenen işlemleri ayıklama ve idempotency üzerine düşünmeye zorlayan bir programlama modeli sunar.
Bu sorunu aşmanın yollarına dair öneriler duymak isterim.
Kavram ilginç, ama yazı nasıl çalıştığını iyi açıklayamıyor
Mevcut durumun olay akışından verimli biçimde nasıl yeniden oluşturulduğunu ve olay akışının veritabanında nasıl modellendiğini merak ediyorum
https://www.youtube.com/watch?v=gG6DGmYKk4I
https://www.youtube.com/watch?v=jnDchr5eabI
https://www.youtube.com/watch?v=ArcypYS5XBQ
https://www.youtube.com/watch?v=uODSwR2CIV4
GitHub’da örnekleri de yönetiyor
https://github.com/oskardudycz/EventSourcing.NetCore
https://github.com/oskardudycz/EventSourcing.NodeJS
https://github.com/oskardudycz/EventSourcing.JVM
Birincisi, bu kullanım için tasarlanmış bir veritabanı kullanmak. Google BigQuery, Amazon Redshift, ClickHouse vb. var
Mevcut tüm veriler özünde bir tür agregasyondur. Başka bir deyişle, olay veritabanı üzerinde yapılan bir group-by sorgusu gibidir
Olaylar varsa, agregasyon sorgusuyla mevcut durumu ya da geçmiş bir durumu teknik olarak yeniden oluşturabilirsiniz; bu yüzden mantıklı
İkincisi, ilişkisel deponun adını değiştirip olay sisteminin yanında duran bir önbellek katmanı demek
İşlevsel olarak aynı şeydir, ama her şeyi olay güdümlü yapmak gerektiğine takıntılı kişilerde alarm zilleri çaldırmaz
Yazıda tarif edilen mimari gerçekten var. Ancak son derece karmaşık olduğu için, onu kullanan servisler genellikle çok hedefli işler yapar. Google Analytics, Datadog, Splunk gibi şeyleri düşünün
Farklı gereksinimlere göre farklı sistemlerde farklı durumlar oluşturabilirsiniz
Bir alışveriş sistemi yaptığınızı varsayalım; satın almalar ve müşteriler olduğunda, bir servis olayları okuyup finansal amaçlar için ilişkisel tablolar oluşturabilir
Başka bir servis olayları okuyup müşteri verileri için bir anahtar-değer deposu oluşturabilir, üçüncü bir servis de ürün araması için bir OpenSearch servisi çalıştırabilir
Olay akışı bir listedir. Kafka gibi amaca uygun bir şey kullanırsanız, birden çok liste; yani topic’ler, partition’lar vb. olur
Ama bu da ilişkisel model içinde çözülebilir
Bu, yukarıdan aşağıya vs aşağıdan yukarıya, ya da özel amaçlı vs genel amaçlı farkı
Yukarıdan aşağıya yaklaşım, iş alanından başlayıp uygulamayı mevcut teknolojiler, araçlar ve tedarikçilerin üzerine eşlemektir
Aşağıdan yukarıya yaklaşım ise mevcut teknolojiler, araçlar ve tedarikçilerden başlayıp bunları bir araya getirerek çalışan bir çözüm üretmektir
Özel amaçlı tarafta DDD, CQRS/ES, Sagas, TBUI (görev tabanlı/güdümlü UI), GraphQL, cebirsel veri tipleri vb. var
Genel amaçlı tarafta RDBMS, CRUD, REST, ACID işlemleri, CDC, genel amaçlı yönetici UI’ları, no-code/low-code, sınırlı/genel amaçlı tipler vb. var
Ben eski usul, iyi ilişkisel veriyi kullanmaya devam edeceğim
Olay tabanlı mimariye katılıyorum, ama bu yazı ana fikri aktarmakta zorlanıyor gibi
Veri ilişkileri ile iş eylemleri arasındaki farka odaklanırdım
Eylemler ve iş faaliyetleri açısından düşünmeye başladığınızda, operasyonel ilişkisel veri depolarından uzaklaşma hareketi çok daha belirgin hale geliyor
Event sourcing’in birçok iyi özelliği var, bu yüzden ilginç
Ama yine de ilişkilere ihtiyaç yok mu? Varsa bu ilişkiler nasıl uygulanıyor?
Cevap “hepsi uygulama katmanı kodunda örtük olarak duruyor” ise bunu kabul etmek zor
Yine de ilişkileri sorgulamak, ilişki görünümlerini güncel tutmak ya da buna benzer bir şey gerekiyor
İlişkiler kalıcılık modelinin merkezinde olmasa da sorun değil; ama veri katmanının bir yerinde uygulanmaları gerekir, burada bundan söz edildiğini göremiyorum
Firestore’da da aynı sorun var. Herkes ilişkileri bir şekilde ele alıyor, ama sonuçta ölçeklenemeyen spagetti uygulama koduna dönüşüyor
Fonksiyonel programlamaya aşinaysanız, bu özünde bir olay akışını tek bir duruma katlayan fold işlemiyle aynıdır
Geçmişte event sourcing sistemleriyle çalışmış biri olarak, açıkça saklanan olay geçmişinin avantajı var; ama karmaşıklığı da epey artırıyor
Okuma modelinin pratikte nasıl oluşturulacağı, model sürümlerinin nasıl yönetileceği, okuma modelinin snapshot’ının tutulup tutulmayacağı gibi sorunlar ortaya çıkıyor
Deneyimime göre, bu kalıbın uygulandığı bağlamların çoğunda ek karmaşıklık buna değmedi
Gereken şey bir komut kuyruğu. Komut olayı, domain olayı değildir