Domain Olayları — Hesap Açılmadı, Hoş Geldin E-postası Neden Gitti?
Önce şunu oku: Zengin Domain Modeli — JPA Entity'de Kapsülleme
30 saniyede özet
Hesap açılınca e-posta ve puan gibi yan işler olur. Servisin hepsini çağırması onu herkese bağlar; olay kullanmak kodu ayırır ama hatayı ayırmaz. Geri alınamayan yan işler commit'ten sonra çalışan bir dinleyiciye gider.
Bir kulübün kayıt görevlisi her yeni üyede posta odasını ve hediye masasını tek tek arıyor. Bir gün kayıt son anda iptal oluyor, ama hediye çoktan yola çıkmış. Başka bir kulüpte görevli yalnızca panoya bir not asıyor: “yeni üye katıldı”.
-
Bayt: Hesap açma servisi önce kaydı yazıyor, sonra e-posta ve puan servisini çağırıyor.
-
Sen: Bugün puan servisi çöktü. Kimse hesap açamadı; ama herkese hoş geldin e-postası gitti!
-
Bayt: Puan bir hediye. Hediye yok diye müşteri bankaya giremiyor mu?
-
Bayt: Kayıt kesinleşince panoya bir not asalım; ilgilenen okusun.
Asıl iş herkesi tanımasın
Hesap servisi e-postayı, puanı ve risk kontrolünü kendisi çağırırsa, her yeni yan iş onu değiştirir. Servis, ilgisi olmayan servisleri tanımak zorunda kalır.
Bir domain olayıDomain'de olmuş bir şeyi geçmiş zamanla anlatan nesne: AccountOpened. Kimin dinleyeceğini bilmez.Sözlükte gör → bunu ayırır. aggregateBirlikte tutarlı kalması gereken ve tek bir kök nesne üzerinden değiştirilen nesne kümesi. Dışarıdan yalnızca köke erişilir; başka aggregate'lara id ile referans verilir.Sözlükte gör → “AccountOpened” olayını kaydeder; e-posta ve puan bu olayı dinler. Hesap servisi artık onların varlığını bilmez.
Kafam karıştı, daha basit anlat
Asıl iş yalnızca olanı söyler. Yan işler o olayı dinler.
Domain olayı nedir?
Hesap servisi e-posta, puan ve risk servisini doğrudan çağırıyor. Sorun nedir?
Kodu ayırmak, hatayı ayırmak değil
Olayı @EventListener ile dinliyoruz; dinleyici aynı transaction içinde çalışıyor. Puan servisi hata verdi. Hesap açılır mı? Cevabı göster
Açılmaz. Dinleyici aynı transaction içinde çalıştığı için hata yukarı çıkar ve hesabın kaydı da geri alınır. Kod ayrıldı, ama hata ayrılmadı; e-posta da çoktan gitmişti.
Adım adım oku
- Kayıt görevlisi herkesi tek tek arıyor.
- Kayıt iptal oldu, hediye çoktan gitti.
- Panoya not: yeni üye katıldı.
- Kayıt kesinleşince panoya as; ilgilenen okusun.
Spring’in @TransactionalEventListenerBir olayı transaction'ın belli bir aşamasında, varsayılan olarak commit'ten sonra işleyen Spring dinleyicisi.Sözlükte gör →’ı olayı commit’ten sonra işler. Commit başarısız olursa dinleyici hiç çalışmaz; açılmayan hesaba e-posta gitmez.
Dinleyicideki hata da artık asıl işi geri almaz. Hesap açık kalır, puan sonra yeniden denenir.
Kafam karıştı, daha basit anlat
Geri alınamayan yan işi commit’ten sonra yap. Yan işin hatası asıl işi bozmasın.
@EventListener aynı transaction içinde çalışırken puan servisi hata verirse ne olur?
@TransactionalEventListener(phase = AFTER_COMMIT) ne sağlar?
Kendin gör
Hesap açılınca e-posta ve puan
Tohum 822029Hesap servisinin doğrudan tanıdığı yan servis: 2
Şu an ne oldu?
Servis e-postayı ve puanı kendisi çağırıyor
Puan servisi çöktü
Görevler0/3
Açılmayan hesaba hoş geldin e-postası gitsinaçık
İpucu
Commit başarısız olsun.
Puan servisi yüzünden hesap açılamasın, olayla bileaçık
İpucu
Aynı transaction içinde dinleyici.
Puan servisi çökse de hesap açılsınaçık
İpucu
Commit sonrası dinleyici.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. Servis her şeyi kendisi çağırıyor, puan servisi çöktü: hesap açılmadı, e-posta yine de gitti.
- “@EventListener, aynı transaction” seç. Servis artık kimseyi tanımıyor; ama sonuç aynı.
- “Commit başarısız” seç. Hesap yok, e-posta ve puan gitti.
- “AFTER_COMMIT” seç ve iki hatayı da dene. Commit başarısızsa kimseye bir şey gitmez; puan servisi çökse de hesap açılır.
E-posta commit'ten önce gönderildi, sonra commit başarısız oldu. Ne olur?
Spring Data'da bir aggregate olayı nasıl kaydeder?
Tuzaklar
Kesin teslim beklemek. Commit sonrası dinleyici çalışırken uygulama çökerse olay kaybolabilir. Kaybolmaması gereken olaylar aynı transaction’da bir outboxOlayı, iş verisiyle aynı yerel transaction içinde bir tabloya yazıp ayrı bir süreçle kuyruğa taşıma deseni. İki kaynağa yazma (dual write) problemini çözer.Sözlükte gör → tablosuna da yazılır.
Commit sonrası veritabanına yazmak. O anda asıl transaction bitmiştir. Dinleyici yazacaksa yeni bir transaction açmalıdır (REQUIRES_NEW).
Her şeyi olaya taşımak. Asıl işle birlikte olması ya da hiç olmaması gereken bir iş (örneğin ledger satırı) olaya bırakılmaz; aynı transaction içinde yapılır.
Kafam karıştı, daha basit anlat
Kaybolmaması gerekeni outbox’a yaz, commit sonrası yazarken yeni transaction aç, ayrılmaz işi ayırma.
AFTER_COMMIT dinleyicisi çalışırken uygulama çökerse olay ne olur?
Derinleş · Hesap açılışı: olay commit'ten sonra 5 dosya · ~81 satır · ilk okumada atlayabilirsin
Kendini sına
Domain olayı, kimin dinleyeceğini bilmeden olanı anlatır.
AFTER_COMMIT dinleyicisi veritabanına yazmak istiyor. Dikkat edilmesi gereken nedir?
Aklında kalacak üç şey
- 1 Domain olayı olmuş bir şeyi geçmiş zamanla anlatır. Asıl iş, kimin ilgileneceğini bilmek zorunda kalmaz.
- 2 Aynı transaction içinde çalışan dinleyici kodu ayırır ama hatayı ayırmaz: yan işteki hata asıl işi de geri alır.
- 3 E-posta gibi geri alınamayan yan işler commit'ten sonra yapılır. Kesin teslim gerekiyorsa olay bir outbox tablosuna da yazılır.