WorkBildirim ve uygulama mimarisi
Genişletilebilir Bildirim Yönlendirme Altyapısı
Büyüyen switch-case bloklarını bağımsız bildirim aksiyonlarına dönüştüren; push bildirimlerini uygulama durumuna göre güvenli ve ölçeklenebilir biçimde yöneten Flutter altyapısı.
Problem
Uygulamanın ilk dönemlerinde az sayıda bildirim tipi vardı. Payload içindeki tür ya da rota değeri
okunuyor, merkezi bir switch-case bloğunda ilgili ekran açılıyordu. Birkaç senaryo için bu sade ve
anlaşılır bir yaklaşımdı. Sağlık uygulamasına randevu, reçete, sohbet, cihaz, hatırlatıcı, sağlık
raporu, görüntülü görüşme gibi yeni alanlar eklendikçe aynı blok da büyüdü.
Her yeni bildirim tanımında merkezi yapıya dokunmak gerekiyordu. Yeni bir case, rota tanımının
yanında payload ayrıştırmayı, mevcut ekranın kontrolünü, doğru navigasyon yığınını bulmayı ve bazen
sayfayı yenilemeyi de aynı yere getiriyordu.
Zamanla şu sorunlar ortaya çıktı. Bir bildirim için yapılan değişiklik başka bildirim akışlarını etkileyebiliyordu. Merkezi dosya çok sayıda modüle bağımlı hale geldi ve farklı yönlendirme kuralları iç içe koşullarla yönetilmeye başlandı. Kod incelemesinde küçük bir bildirim eklemesi büyük ve riskli bir değişiklik gibi görünüyordu. Tek bir bildirimi bağımsız test etmek zorlaştı, aynı payload kontrolü de farklı case’lerde tekrar edebiliyordu.
switch-case sözdiziminin kendisinde bir sorun yoktu. Sorun, sistem büyüdükçe bütün bildirim
davranışlarının tek bir karar noktasına bağlanmasıydı.
Hedef
Her yeni bildirim türünde mevcut yönlendirme kodunu açıp değiştirme ihtiyacını ortadan kaldırmak istedim. Altyapı gelen mesajı alacak, mesajın hangi koşulda çalışacağına ve ne yapacağına ise ilgili bildirim sınıfı karar verecekti.
Yeni yapıyı şu ilkelere göre kurdum:
- Her bildirim davranışı tek bir sınıfta izole edilmeli.
- Sınıf desteklediği payload’ı tanımalı ve kendi aksiyonunu çalıştırmalı.
- Merkezi yönetici bildirim tiplerini bilmemeli, yalnızca uygun aksiyonu bulmalı.
- Yeni bir tür eklemek mevcut aksiyonların koduna dokunmayı gerektirmemeli.
- Uygulama kapalıyken, arka plandayken ya da ön plandayken gelen bildirimler aynı giriş noktasında birleşmeli.
- Hatalı ya da bilinmeyen bir payload uygulamayı bozmamalı.
Eski yapıdan yeni yapıya
Önceki yaklaşımda akış merkezi bir karar bloğuna bağlıydı:
Bildirim → türü oku → switch-case → rota ve özel davranış
Yeni yaklaşımda bildirim, ortak sözleşmeyi uygulayan aksiyonlar arasında değerlendirilir:
FCM veya yerel bildirim
↓
Ortak payload işleyici
↓
Notification Navigator
↓
Eşleşen Notification Action
↓
Payload doğrulama + güvenli yönlendirme
Navigator artık uzun bir koşul listesi çalıştırmıyor. Kayıtlı aksiyonlara sırayla “Bu bildirimi sen mi yönetiyorsun?” diye soruyor ve ilk eşleşen sınıfı çalıştırıyor. Bu, Strategy ve Chain of Responsibility kalıplarının sade bir birleşimi.
Ortak bildirim sözleşmesi
Bütün bildirim aksiyonları aynı küçük sözleşmeye uyuyor. Bir aksiyon iki soruya cevap veriyor: gelen veri onun sorumluluğunda mı, ve eşleşiyorsa ne yapılmalı?
Örneğin sohbet bildirimi kendi tür ve rota eşleşmesini kontrol ediyor. Kullanıcı zaten sohbet ekranındaysa içeriği yeniliyor, başka bir ekrandaysa doğru navigasyon yığınını hazırlayıp sohbet sayfasını açıyor. Cihaz bildirimi aynı sözleşmeyi kullanıyor ama bambaşka bir modüle ve rota davranışına sahip.
Bildirim yöneticisi sohbetin, reçetenin ya da randevunun iş kurallarını bilmiyor. Her sınıf yalnızca kendi bildirim senaryosu değiştiğinde değişiyor.
Bildirim yaşam döngülerini tek noktada birleştirmek
Bildirim yalnızca kullanıcı uygulamanın dışındayken gelmiyor. Mesaj uygulama kapalıyken, arka plandayken ya da kullanılırken gelebiliyor. Android’de ön plandaki mesajın yerel bildirim olarak gösterilmesi, kapalı uygulamayı açan ilk mesajın işlenmesi ve kullanıcının bildirime dokunmasının dinlenmesi ayrı platform olayları.
Bu yaşam döngülerini Firebase Messaging istemcisi yönetiyor ve elde ettiği payload’ı aynı navigator’a gönderiyor. Action sınıflarının uygulamanın hangi sistem olayıyla açıldığını bilmesi gerekmiyor; platform entegrasyonu ile ürünün yönlendirme davranışı ayrı kalıyor.
Token alma, token yenileme, bildirim izni ve çıkışta aboneliklerin kapatılması gibi altyapı işleri de bu sayede rota kararlarına karışmıyor.
Payload eşleştirme ve doğrulama
Payload dış sistemden gelen dinamik bir veri. Beklenen bir alan eksik, değer eski ya da biçim hatalı olabilir. Bu yüzden bir bildirimi yalnızca rota adına bakarak koşulsuz çalıştırmak yetmiyor.
Her action önce genel bildirim türünü, sonra sorumlu olduğu rota değerini kontrol ediyor. Eşleşme sağlanınca yalnızca ihtiyaç duyduğu alanları okuyor. Detay ekranına giden bir bildirim kayıt kimliğine ihtiyaç duyarken liste ekranına giden bir bildirim hiç ek veri istemeyebilir.
Ayrıştırma sırasında oluşan hatalar navigator sınırında yakalanıp teknik kayda dönüştürülüyor. Bilinmeyen bir bildirim türü için eşleşme bulunamazsa kullanıcı yanlış bir ekrana gönderilmiyor ve uygulama çökmüyor.
Navigasyon durumunu korumak
Hedef ekranı açmak tek başına yeterli olmuyor. Kullanıcı zaten o ekranda olabilir, hedef modül navigasyon yığınında bulunabilir ya da uygulama bambaşka bir akışta olabilir. Her seferinde yeni sayfa açmak yinelenen ekranlara ve bozuk geri navigasyonuna yol açabilir.
Bu yüzden action sınıfları duruma göre karar veriyor. Hedef ekran açıksa yeni ekran eklemek yerine içeriği yeniliyor. Hedef modül yığında varsa o yığına dönüp gereken sayfayı açıyor, modül aktif değilse önce güvenli bir başlangıç noktasına dönüyor. Gerekli veriyi payload’dan çıkarıp detay rotasına aktarıyor. Kullanıcı aktif bir görüşmedeyse ikinci bir görüşme ekranı açmıyor.
Bu kararlar ilgili bildirimin sınıfında kaldığı için navigator büyümüyor. Karmaşık bir bildirim yalnızca kendi dosyasını karmaşıklaştırıyor.
Modüler mimariyle uyum
Uygulama randevu, sohbet, cihazlar, reçeteler, hatırlatıcılar, dosyalar ve sağlık raporları gibi
bağımsız modüllerden oluşuyor. Merkezi switch-case zamanla bütün modüllerin rota ve iş ayrıntılarını
bilen bir bağımlılık düğümüne dönüşmüştü.
Sınıf tabanlı yapıda her action, ilgili modülün yönlendirme ihtiyacını kendi içinde tutuyor. Ortak çekirdek katman yalnızca bildirim sözleşmesini tanımlıyor, somut aksiyonları uygulama katmanı bir araya getiriyor. Temel mimari modüllerin ekran ayrıntılarını bilmeden çalışabiliyor.
Event Bus kullanılan akışlarda action ekran bileşenine doğrudan erişmiyor, ilgili modüle bir olay gönderiyor. Bu, özellikle açık sayfayı yenileme ya da dashboard yüklendikten sonra modüller arası geçiş senaryolarında bağımlılığı azaltıyor.
Değişime açık, bozulmaya kapalı tasarım
Bu dönüşüm Açık/Kapalı Prensibi’nin pratikteki bir karşılığı oldu: sistem yeni bildirim davranışlarına açık, mevcut davranışları taşıyan sınıfların sürekli değiştirilmesine kapalı.
Yeni bir tanım geldiğinde geliştirici merkezi koşul bloğunda doğru yeri aramıyor. Yeni action sınıfı:
- Kendi eşleşme kuralını tanımlar,
- İhtiyaç duyduğu payload alanlarını doğrular,
- Kendi navigasyon veya event davranışını uygular,
- Navigator’ın aksiyon listesine kaydedilir.
Kayıt listesi yalnızca sisteme hangi yeteneklerin dahil olduğunu gösteren bir composition noktası, bildirimlerin iş kurallarını içermiyor. Yeni bir sınıf eklemek mevcut sınıfların davranışını değiştirmediği için regresyon alanı da daralıyor.
Test edilebilirlik
Eski yapıda tek bir bildirimi test etmek için büyük yönlendirme fonksiyonunun farklı koşullarını hazırlamak gerekiyordu. Şimdi her action ayrı ayrı test edilebiliyor:
- Doğru tür ve rota için eşleşiyor mu?
- Başka bir bildirimi yanlışlıkla sahipleniyor mu?
- Eksik payload durumunda güvenli davranıyor mu?
- Hedef ekran açıksa yenileme davranışını çalıştırıyor mu?
- Hedef ekran açık değilse doğru rotayı oluşturuyor mu?
Navigator için daha küçük bir sözleşme testi yetiyor: kayıtlı aksiyonlar arasında ilk eşleşeni bulmak, onu bir kez çalıştırmak ve bilinmeyen payload’da sorunsuz tamamlanmak.
Sonuç
Bildirim altyapısı büyüyen bir switch-case bloğundan, her biri kendi sorumluluğunu taşıyan action
sınıflarına geçti. Randevu, sohbet, reçete, cihaz, sağlık raporu ve diğer bildirim türleri aynı giriş
noktasını kullanıyor ama yönlendirme kararlarını ayrı ayrı veriyor.
Yeni bir bildirim eklemek artık ortak sözleşmeye uyan yeni bir sınıf yazmak demek; mevcut koşul ağacına dokunulmuyor. Kod incelemeleri küçüldü, olası regresyonlar daha dar bir alana çekildi ve ekipler kendi modüllerinin bildirim akışlarını diğerlerini bozmadan geliştirebiliyor.
Öğrendiklerim
switch-caseaz sayıda sabit durum için uygun. İş kuralları büyüdüğünde sorun, bütün değişim nedenlerinin aynı yerde toplanması oluyor.- Eşleşme, veri doğrulama ve yönlendirme kararı aynı sınıfta durunca her bildirimin değişiklik sınırı netleşiyor.
- Merkezi yöneticinin iş ayrıntılarını bilmesine gerek yok. Görevi doğru aksiyonu bulup çalıştırmak.
- Composition noktası ile iş mantığını ayrı tutmak gerekiyor. Yeni sınıfı listeye kaydetmek, karar ağacını büyütmeden sisteme yeni bir yetenek ekliyor.
- Kapalı, arka plan ve ön plan bildirimleri tek bir giriş noktasında aynı sözleşmeye ulaştığında davranış tutarlı oluyor.
- Bildirime dokunmak her zaman yeni ekran açmak anlamına gelmiyor; bazen doğru davranış açık ekranı yenilemek.
- Bilinmeyen payload kontrollü biçimde yok sayılmalı. Dışarıdan gelen veri uygulamayı kararsız hale getirmemeli.