Bildirimler tek satır kodla başlar: sipariş kargoya verilince bir e-posta gönder. Bir yıl sonra aynı şirket bir düzine servisten push, e-posta ve SMS gönderir, kullanıcılar aynı uyarıyı üç kez alır, şifre sıfırlama e-postasının iletilip iletilmediğini kimse söyleyemez ve pazarlama mesajları insanları gece 3'te uyandırır.
Bir bildirim sistemi, bir şeyin olduğuna karar veren kodla mesajları ileten sağlayıcıların arasına tek bir servis koyarak bunu düzeltir. Bu yazı onu tasarlıyor: API, herhangi bir şey gönderilmeden önce çalışan kontroller, yeniden denemelerle kanal başına iletim ve takip. Sona yakın bir yerdeki diyagram akışın tamamını gösteriyor.
Gereksinimler
- Ürün servisleri, kullanıcının hangi kanalları açtığını bilmeden tek bir çağrıyla bildirim gönderir.
- Dile göre şablonlarla push (iOS ve Android), e-posta ve SMS.
- Kullanıcılar kanalları ve konuları kontrol eder, sessiz saatlere uyulur.
- Aynı olay asla aynı mesajı iki kez üretmez.
- Her mesajın bir durumu vardır: gönderildi, iletildi, geri döndü, başarısız.
- Yavaş bir sağlayıcı diğer kanalları asla geciktirmez.
API: mesajlar değil, olaylar
Ürün servisleri e-posta oluşturmamalıdır. Olguları ve bir idempotency anahtarını içeren bir olay gönderirler:
POST /notifications
{
"idempotencyKey": "order-8812-shipped",
"userId": "u_7",
"type": "order_shipped",
"data": {"orderId": "8812", "carrier": "UPS", "eta": "2026-10-30"}
}
Gerisinin hepsi bildirim servisine aittir: bu türün hangi kanalları kullandığı, hangi şablon, hangi dil ve kullanıcının bunu isteyip istemediği. Bir ekip metni değiştirmek ya da bir bildirim türüne SMS eklemek istediğinde sipariş servisinde hiçbir şey değişmez.
Bir şey gönderilmeden önce
Bir iş bir kanala ulaşmadan önce API'de bu sırayla dört kontrol çalışır:
- Kopyaları ayıklayın. Idempotency anahtarını bir TTL ile saklayın ve tekrarları atın. Yukarı akış servislerinde yeniden denemeler normaldir ve kullanıcılara kopya olarak ulaşmamalıdır.
- Tercihleri kontrol edin. Kullanıcının kanallarına, konu bazlı abonelik iptallerine ve sessiz saatlerine bakın. Şifre sıfırlama gibi güvenlik uyarıları genellikle pazarlama iptallerini atlar; pazarlama ise asla atlamaz. Bu kuralı yazıya dökün.
- Hacmi sınırlayın. Kullanıcı, kanal ve saat başına mesajları sınırlayın. 500 olay tetikleyen bir hata 500 push bildirimine dönüşmemeli.
- İşleyin. Bildirim türü, kanal ve kullanıcının dili için şablonu seçin ve olay verisiyle doldurun.
Sessiz saatler kullanıcının saat dilimini gerektirir. Zamana duyarlı değilse mesajı atmak yerine bekletin ve pencerenin sonuna zamanlayın.
Kanal başına bir kuyruk ve bir işçi havuzu
Kontrollerden sonra API, her kanal için o kanalın kuyruğuna bir iş yazar. Ayrı kuyruklar tasarımın en önemli kararıdır: SMS sağlayıcısı yavaşladığında SMS işleri kendi kuyruklarında birikir, push ve e-posta akmaya devam eder.
Kanal işçileri sağlayıcıları çağırır ve onların tuhaflıklarıyla başa çıkar:
- Push. iOS cihazları Apple Push Notification service, Android cihazları FCM HTTP v1 API üzerinden geçer. Cihaz token'ları eskir: APNs artık etkin olmayan bir token için 410 yanıtı verir, FCM ise unregistered hatası döner. Bu token'ları silin; yoksa kimsenin almadığı mesajlar için gecikme ödemeye devam edersiniz.
- E-posta. Bir sağlayıcı üzerinden gönderin ve geri dönmeleri ile şikâyetleri geri raporlayın. Toplu e-posta gönderiyorsanız RFC 8058 ile tanımlanan tek tıkla abonelikten çıkma başlıklarını ekleyin. Gmail'in gönderici yönergeleri, günde 5.000'den fazla mesaj gönderenlerin pazarlama ve abonelik mesajları için tek tıkla abonelikten çıkmayı zorunlu tutar.
- SMS. Mesaj başına en pahalı ve en çok düzenlenen kanal. Kodlar ve acil uyarılar için saklayın ve gönderdiğiniz her ülkenin kurallarını kontrol edin (bu yazı hukuki tavsiye değildir).
İşçiler geçici hataları üstel backoff ve jitter ile yeniden dener ve belirli sayıda denemeden sonra bir işi dead letter kuyruğuna taşır. Geçersiz adres gibi kalıcı hatalar yeniden denenmez.
İletimi takip etmek
Sağlayıcılar ne olduğunu webhook'lar ya da yanıt kodlarıyla bildirir: kabul edildi, iletildi, geri döndü, açıldı. Her durum değişikliğini bildirim ID'sine göre anahtarlanmış bir iletim loguna yazın. Böylece destek ekibi "Kullanıcı sıfırlama e-postasını aldı mı?" sorusunu saniyeler içinde yanıtlayabilir, siz de kanal ve sağlayıcı başına iletim oranlarını ölçebilirsiniz.
Durum güncellemeleri sırasız ve bazen iki kez gelir. Bir durum sütununun üzerine yazmak yerine onları zaman damgalı olaylar olarak saklayın ve güncel durumu bunlardan türetin.
Uygulama içi bildirimler
Birçok ürün ayrıca uygulama içi bir gelen kutusu tutar: sayaçlı zil simgesi. Onu, bir sağlayıcıyı çağırmak yerine bir veritabanına yazan dördüncü bir kanal olarak ele alın. Kullanıcı ve bildirim başına okundu bayrağı olan bir satır saklayın, kullanıcıya ve zamana göre indeksleyin ve uygulamanın son sayfayı ve okunmamış sayısını çekmesine izin verin. Öğeleri toplu olarak okundu işaretleyin ki gelen kutusunu açan bir kullanıcı elli yazmayı tetiklemesin.
Kaba hesaplar
Bir milyon günlük aktif kullanıcı ve kullanıcı başına günde beş bildirimle sistem günde 5 milyon bildirim gönderir; bu ortalama saniyede yaklaşık 58 eder. Önemli olan zirvelerdir: bir kampanya ya da bir olay uyarısı birkaç dakikada bir milyon mesaj gönderebilir. Kuyrukları patlamaya göre boyutlandırın, işçilerin onları her sağlayıcının izin verdiği hızda boşaltmasına izin verin ve sağlayıcıların sizi kendi işçilerinizin kapasitesi bitmeden önce kısıtlamasını bekleyin.
Mimari diyagram
Diyagram içeriği
- Product services
- Notification API
- Preferences
- Push queue
- Email queue
- SMS queue
- Push workers
- Email workers
- SMS workers
- APNs and FCM
- Email provider
- SMS provider
- Delivery log
- event
- opt-outs, quiet hours
- status
- webhooks
Nasıl okunur:
- Sol: ürün servisleri idempotency anahtarı olan olayları bildirim API'sine gönderir. API tercih deposunu okur ve Markdown bloğunda listelenen kontrolleri çalıştırır.
- Orta: her kanalın kendi kuyruğu ve işçi havuzu vardır; böylece yavaş bir sağlayıcı yalnızca kendi kanalını tıkar.
- Sağ: işçiler APNs ve FCM'i, e-posta sağlayıcısını ve SMS sağlayıcısını çağırır. Durum güncellemeleri sonradan ve asenkron geldiği için iletim loguna noktalı kenarlarla akar.
Diyagrama neler koymalı
- Kontroller ve sıraları. Kopya ayıklama, tercihler, sınırlar ve işleme sistemin gerçek mantığıdır.
- Kanal başına bir şerit. Diyagramınızda tüm kanallar tek bir kuyruğu paylaşıyorsa, muhtemelen üretimde de paylaşıyordur.
- Yeniden deneme ve dead letter davranışı. Her işçinin üzerine backoff'u ve deneme sınırını not edin.
- Geri dönen durum yolu. O olmadan kimse kayıp bir mesajın hatasını ayıklayamaz.
Kuyruk ve tüketici tarafının ayrıntıları için olay güdümlü mimari yazısına bakın. Mikroservis mimari diyagramı bunun gibi paylaşılan bir servisin diğerlerinin arasında nerede durduğunu gösterir; iletişim bilgilerini ve iletim loglarını saklamadan önce GDPR veri haritalama yazısını okumaya değer.
Archboard'da oluşturun
- Google ya da GitHub ile giriş yapın ve bir diyagram oluşturun.
- C4 Modeli paketinden ürün servisleri için bir Konteyner, bildirim API'si için bir tane daha ve API'nin altına tercihler için bir Veritabanı ekleyin.
- Veri paketinden kanal başına bir tane olmak üzere üç Kuyruk düğümü ve her kanalın işçileri için bir Konteyner ekleyin.
- Sağlayıcılar için C4 Modeli paketinden üç Harici sistem düğümü ve iletim logu için bir Veritabanı ekleyin.
- Durum kenarlarını noktalı çizgilerle çizin ve gönderim öncesi kontrolleri içeren, Temel paketinden bir Markdown bloğu ekleyin.
Örnek 14 diyagram düğümü kullanıyor ve Free planın sınırları içinde kalıyor.
SSS
Neden her servis kendi bildirimlerini göndermesin? Çünkü o zaman tercihler, sessiz saatler, kopya ayıklama ve iletim takibi her serviste ayrı ayrı yazılmak zorunda kalır ve birbirinden uzaklaşır. Tek bir bildirim servisi bunları her yerde aynı şekilde uygular.
Kopya bildirimleri nasıl durdururum? Her istekte bir idempotency anahtarı zorunlu tutun ve bir süre saklayın. Tekrarlanan anahtar onaylanır ve atılır.
Push, e-posta ve SMS aynı kuyruğu paylaşmalı mı? Hayır. Ayrı kuyruklar ve işçi havuzları, yavaş ya da hata veren bir sağlayıcının diğer kanalları geciktirmesini önler.
Sessiz saatleri nasıl ele alırım? Her kullanıcının saat dilimini ve sessiz penceresini saklayın ve acil olmayan mesajları göndermek ya da atmak yerine pencerenin sonuna zamanlayın.
Bir bildirimin iletildiğini nasıl bilirim? Sağlayıcı yanıtlarını ve webhook'larını bildirim ID'sine göre anahtarlanmış bir iletim loguna kaydedin. İletim her kanalda farklı anlamlara gelir; bu yüzden sağlayıcının kendi durum adlarını kaydedin.