Event Storming Workshop

TicketHub — Online Etkinlik Bilet Satis Platformu
Bir oda, bir duvar, renkli post-it'ler, 3-5 kisilik ekip

Renk Kodlari & Ok Turleri

Her post-it rengi farkli bir anlam tasir. Oklar ise "kim kimi tetikliyor, nasil" sorusunu yanitlar.

Turuncu — Domain Event
"Bir sey OLDU" (gecmis zaman). Sistemdeki olgular.
Ornek: "Bilet Rezerve Edildi", "Odeme Alindi"
Mavi — Command
"Bir sey YAP" (emir kipi). Niyet, istek.
Ornek: "Koltugu Sec", "Odemeyi Yap"
Sari — Actor / User
"Kim yapiyor?" Komutu veren insan.
Ornek: "Musteri", "Organizator", "Admin"
Yesil — Read Model
"Kullaniciya ne gosteriyoruz?"
Ornek: "Musait koltuk haritasi", "Fiyat gecmisi"
Pembe — External System
"Dis servis" — bizim kontrolumuzde degil.
Ornek: Stripe, SMS, Email servisi
Kirmizi — Hot Spot
"Sorunlu / belirsiz / tartismali / riskli nokta"
Ornek: "Ayni koltuğa 2 kisi tiklarsa?"

Ok Turleri — "Kim Kimi Nasil Tetikliyor?"

Insan tetikler
Sari (Actor) → Mavi (Command)
Kullanici bilinçli bir aksiyon baslatir
Sonucunda olur
Mavi (Command) → Turuncu (Event)
Komut basarili olursa event olusur
Otomatik tetikler (Policy)
Turuncu (Event) ⇢ Mor (Policy) ⇢ Turuncu (Event)
Insan YOK — sistem kurali devreye girer
Dis servis cagrisi
Turuncu (Event) → Pembe (External)
Stripe, SMS vb. dis sisteme gider
Zaman akisi
Event → Event (kronolojik sira)
Sadece "sonra" anlaminda

Mor Post-it (Policy) Nedir?

Tek cumleyle: "Bir event olunca, INSAN MUEDAHALESI OLMADAN, otomatik calisan kural."

Normal akis: Insan bir sey yapmak ister → Command gonderir → Event olusur.
Policy akisi: Event olusur → Policy OTOMATIK tetiklenir → yeni Event veya Command olusur.

Aradaki fark: policy zincirinde kullanici YOK. Sistem kendi kendine karar veriyor. Bu yuzden ok KESIKLI ciziliyor — "bu adim otomatik, biri tiklamadi" anlami tasiyor.

👤 Insan Tetikli Akis (DUZ ok)

Musteri Koltugu Sec Koltuk Secildi
Musteri bilinçli tikliyor → komut gonderiyor → event olusuyor.
Her adimda bir INSAN karari var.

⚡ Policy Tetikli Akis (KESIKLI ok)

Koltuk Secildi 10dk zamanlayici kur Suresi Doldu
Kimse tiklamadi. Event oldu → sistem otomatik kurali calistirdi → yeni event oldu.
BullMQ delayed job, cron, veya if-then kurali.

TicketHub'daki Tum Policy'ler

🔸 WHEN: Koltuk secildi (hold olusturuldu)
⚡ THEN: 10 dakika sonra expire et
→ BullMQ delayed job (600.000ms)
🔸 WHEN: Odeme basariyla alindi
⚡ THEN: Rezervasyonu onayla
→ reservation.confirmPayment()
🔸 WHEN: Rezervasyon onaylandi
⚡ THEN: QR bilet uret + email gonder
→ asyncEventBus.emit() × 2
🔸 WHEN: Rezervasyon olusturuldu (talep degisti)
⚡ THEN: Fiyati yeniden hesapla
→ pricingService.applySurge()
🔸 WHEN: Kalan koltuk < %20
⚡ THEN: Surge fiyat uygula (x1.3)
→ PRICING_RULES.LOW_AVAILABILITY
🔸 WHEN: Bilet iptal edildi + onaylanmisti
⚡ THEN: Odemeyi iade et
→ paymentsService.refund()

Policy = kodda nereye denk dusuyorlar?
• BullMQ delayed job (zamanlayici policy'ler) → asyncEventBus.emit('...', payload, { delay: 600000 })
• Sync event handler (aninda policy'ler) → syncEventBus.on('reservation.created', handler)
• Entity icindeki if-then (is kurali policy'ler) → if (status !== 'PENDING') throw

1

Kaotik Kesif Chaotic Exploration

Herkes duvara turuncu post-it yapiyor: "Sistemde ne OLDU?" Siralama yok, gruplama yok.

Duvar — kaotik, sirasiz
Event
Kullanici Kayit Oldu
Event
Etkinlik Olusturuldu
Event
Koltuk Secildi
Event
Odeme Alindi
Event
Bilet Uretildi
Event
Rez. Suresi Doldu
Event
Fiyat Degisti
Event
QR Kod Dogrulandi
Event
Bilet Iptal Edildi
Event
Mekan Tanimlandi
Event
Email Gonderildi
Event
Odeme Iade Edildi
Event
Koltuk Duzeni Belirlendi
Event
Giris Yapildi
Event
Surge Fiyat Uygulandi
Event
Etkinlik Yayinlandi
Event
Koltuk Serbest Kaldi
Event
Taban Fiyat Belirlendi
Event
Rezervasyon Onaylandi
Event
Rezervasyon Olusturuldu
2

Kronolojik Siralama Timeline

Event'leri soldan saga zaman sirasina diz.

Kayit & Hazirlik
Event
Kullanici Kayit Oldu
sonra
Event
Giris Yapildi
sonra
Event
Mekan Tanimlandi
sonra
Event
Koltuk Duzeni Belirlendi
sonra
Event
Etkinlik Olusturuldu
sonra
Event
Taban Fiyat Belirlendi
sonra
Event
Etkinlik Yayinlandi
Bilet Satin Alma (en kritik akis)
Event
Koltuk Secildi (Hold)
sonra
Event
Rezervasyon Olusturuldu
sonra
Event
Odeme Alindi
sonra
Event
Rezervasyon Onaylandi
sonra
Event
Bilet Uretildi (QR)
sonra
Event
Onay Emaili Gonderildi
Alternatif Akislar (mutsuz yollar)
Event
Rez. Suresi Doldu
sonra
Event
Koltuk Serbest Kaldi
Event
Bilet Iptal Edildi
sonra
Event
Odeme Iade Edildi
Etkinlik Gunu
Event
QR Kod Okutuldu
sonra
Event
Giris Onaylandi
3

Command + Actor + Policy Enrichment

Her event'e soralim: "Bunu KIM tetikledi? NASIL?" Oklara dikkat — kesikli mor = otomatik (policy), duz mavi = insan tetikli.

Tam Satin Alma Akisi

Actor
Musteri
tetikler
Command
Koltugu Sec
sonucunda
Event
Koltuk Secildi (Hold)
⚡ Hot Spot
500 kisi ayni anda?
Policy
Otomatik hold olustur
otomatik
Event
Rezervasyon Olusturuldu (PENDING)
Policy
10dk zamanlayici kur
BullMQ delayed job
Policy
Fiyat yeniden hesapla
Talep degisti
Actor
Musteri
tetikler
Command
Odemeyi Yap
sonucunda
Event
Odeme Alindi
dis servis
External
Stripe
⚡ Hot Spot
Cift odeme? (idempotency)
Policy
Odeme basariliysa → onayla
otomatik
Event
Rezervasyon Onaylandi (CONFIRMED)
Policy
QR bilet uret
async — BullMQ
otomatik
Event
Bilet Uretildi
Policy
Email gonder
async — BullMQ
Alternatif: 10dk doldu, odeme yapilmadi
Policy
10dk doldu → expire et
BullMQ delayed job tetikledi
otomatik
Event
Rez. Suresi Doldu (EXPIRED)
otomatik
Event
Koltuk Serbest Kaldi
Alternatif: Musteri iptal etti
Actor
Musteri
tetikler
Command
Bileti Iptal Et
sonucunda
Event
Bilet Iptal Edildi
⚡ Hot Spot
12 saat kala iptal?
Policy
Onaylanmissa → iade yap
otomatik
Event
Odeme Iade Edildi

Fiyatlandirma Akisi

Actor
Admin
tetikler
Command
Taban Fiyat Belirle
sonucunda
Event
Taban Fiyat Belirlendi
Policy
Koltuk <%20 → 1.3x
Otomatik surge
otomatik
Event
Surge Fiyat Uygulandi
Read Model
"Fiyat neden 750 TL?"
Tum event'leri kronolojik goster → Event Sourcing buradan cikti
4

Bounded Context Kesfi Context Discovery

Duvardaki post-it'lere bak ve GRUPLAMA yap. "Hangi event'ler birlikte degisiyor?" — Kartlara tikla → klasor yapisi, post-it esleme, haberlesmeler

"Kullanici Kayit Oldu" ve "Koltuk Secildi" birlikte degisiyor mu? → HAYIR. → AYRI context.
"Koltuk Secildi" ve "Rezervasyon Olusturuldu" birlikte degisiyor mu? → EVET. → AYNI context.
"Email Gonderildi" ve "Rezervasyon Onaylandi" birlikte degisiyor mu? → HAYIR. Email cokse bile rezervasyon gecerli. → AYRI context.
Basit
Identity & Access
Kimlik — tikla
Kullanici Kayit Oldu
Giris Yapildi
Token Yenilendi
Actor: Musteri, Organizator, Admin
Pattern: Basit CRUD
Basit
Venue Management
Mekan — tikla
Mekan Tanimlandi
Koltuk Duzeni Belirlendi
Actor: Admin
Pattern: Basit CRUD
Basit
Event Catalog
Etkinlik — tikla
Etkinlik Olusturuldu
Etkinlik Yayinlandi
Pattern: CRUD + Redis cache
Karmasik — Core Domain
Booking
Rezervasyon — tikla
Koltuk Secildi
Rez. Olusturuldu
Rez. Onaylandi
Suresi Doldu
⚡ Hot Spot'lar
Ayni koltuk 2 kisi? • 10dk expire? • Max 6 bilet
Pattern: Entity + VO + Lock + BullMQ
Orta
Pricing
Fiyatlandirma — tikla
Taban Fiyat Belirlendi
Surge Uygulandi
Pattern: Event Sourcing Lite
Orta
Payment
Odeme — tikla
Odeme Alindi
Stripe
Stripe cokerse? Cift odeme?
Pattern: Adapter + Circuit Breaker
Basit
Ticketing
Biletleme — tikla
Bilet Uretildi (QR)
QR Dogrulandi
Pattern: HMAC Crypto
Basit
Notification
Bildirim — tikla
Onay Emaili
Hatirlatma
Pattern: BullMQ Worker + DLQ
5

Context Arasi Iletisim Context Map

KimdenKimeTipMekanizmaNeden bu tip?
BookingPricingSYNCFonksiyon cagrisiFiyat bilgisi ANINDA lazim
BookingPricingASYNCBullMQ event"Talep degisti, fiyati yeniden hesapla"
BookingPaymentSYNCFonksiyon cagrisiOdeme sonucu ANINDA lazim
BookingTicketingASYNCBullMQ eventQR uretimi uzun surebilir
BookingNotificationASYNCBullMQ eventEmail 2sn surur, bekletme
AnyIdentitySYNCJWT middlewareHer istek kimlik dogrulamasi gerektirir
Event CatalogVenueSYNCDB foreign keyEtkinlik mekana aittir
6

Hot Spot ➔ Teknik Karar Risk Resolution

Hot SpotTartismaKararKoda Yansimasi
Ayni koltuğa 500 kisi?DB lock mu? App lock mu?Redis distributed lock (Redlock)acquireSeatLock()
10dk doldu ama odeme tam o anda?Race conditionOptimistic concurrency + sessiz donmereservation.expire()
Ayni odeme 2 kez?Network timeout → retryIdempotency-Key + Redis cacheidempotencyGuard
Stripe cokerse?30sn timeout × 1000 istekCircuit breakerCircuitBreaker
Fiyat neden degisti?Musteri sikayeti, regulasyonAppend-only event logpricing_events
Sahte QR bilet?Fotokopi, manipulasyonHMAC-SHA256 + constant-timegenerateQRPayload()
Karaborsaci 100 bilet?ScalpingKullanici basina max 6MAX_SEATS_PER_BOOKING
2 saat kala iptal?Organizator zarar eder48 saat kuraliCANCELLATION_DEADLINE
7

Ozet: Workshop ➔ Kod Summary

Adim 1
Kaotik Kesif
~20 domain event
➔ Event isimleri belirlendi
Adim 2
Kronolojik Siralama
Zaman akisi
➔ Hangi event hangisinden sonra
Adim 3
Command + Actor + Policy
Kim, ne, neden
➔ Route + service + worker ayirimi
Adim 4
Bounded Context
8 context
➔ 8 modul klasoru
Adim 5
Iletisim Haritasi
Ok ve cizgiler
➔ Sync vs async karari
Adim 6
Hot Spot → Karar
Kirmizi post-it → cozum
➔ Redlock, circuit breaker, idempotency
Adim 7
Context → Kod
Karmasiklik tablosu
➔ Domain model nerede var, nerede yok


    
    
Post-it'lere tikla → kodda ne olduğunu gor kapatmak icin tikla
TicketHub Event Storming Workshop — Alberto Brandolini metodolojisi ile