İçeriğe geç
Haydar Demir

ÇalışmalarAğ ve hata yönetimi

Merkezi Ağ Hata Yönetimi ve Kullanıcı Geri Bildirimi

HTTP, bağlantı, zaman aşımı ve veri ayrıştırma hatalarını merkezi olarak sınıflandırıp kullanıcıya anlaşılır, yerelleştirilmiş ve aksiyon alınabilir mesajlar sunan Flutter altyapısı.

Problem

Bir mobil uygulamada “istek başarısız oldu” pek çok farklı şey demek olabilir. Kullanıcının interneti kesilmiş, sunucu zamanında yanıt vermemiş, oturum süresi dolmuş, backend isteği bir iş kuralı yüzünden reddetmiş ya da başarılı görünen yanıt uygulamanın beklediği modelle uyuşmamış olabilir.

Bu durumların her biri kullanıcı için de farklı bir anlama geliyor. İnterneti olmayan birine “Beklenmeyen bir hata oluştu” demek işe yaramaz. Sunucu hatasını “Bağlantınızı kontrol edin” diye göstermek, kullanıcıyı kendi çözemeyeceği bir sorunla uğraştırır. İptal edilmiş bir isteği hata olarak göstermek de kullanıcıya kendi yaptığı geçişin başarısız olduğu izlenimini verir.

Uygulama büyüdükçe her özellik kendi try-catch bloğunu, hata metnini ve HTTP durum kontrolünü yazmaya başlamıştı. Aynı hata farklı ekranlarda farklı mesajlarla görünüyordu; bazı ekranlar backend mesajını kullanırken bazıları teknik exception metnini kullanıcıya gösteriyordu. Bağlantı ve sunucu hataları birbirine karışıyor, global olarak gösterilen bir hata özellik ekranında ikinci kez çıkabiliyordu. Model ayrıştırma hataları sıradan kullanıcı hatası gibi ele alındığı için teknik ekibe yeterli bağlam ulaşmıyordu. Her yeni modülde aynı hata eşleme mantığı yeniden yazılıyordu.

Sağlık uygulamasında bunun sonucu görsel tutarsızlıkla sınırlı kalmıyor. Kullanıcı bir sağlık kaydını, randevuyu ya da ölçümü gönderdiğinde işlemin tamamlanıp tamamlanmadığını doğru anlamalı. Mesajın tonu kadar kullanıcıyı nereye yönlendirdiği de güveni etkiliyor.

Hedef

Bütün hataları tek bir mesaja çevirmek istemiyorduk. Teknik ayrıntıyı merkezde doğru sınıflandırıp her senaryo için uygun bir ürün davranışı üretmek istiyorduk.

Sistemden beklentilerimiz şunlardı:

  1. HTTP ve istemci kaynaklı hataları tip güvenli biçimde ayırmak,
  2. Backend’in anlamlı iş mesajını mümkün olduğunda korumak,
  3. Teknik exception metinlerini son kullanıcıdan gizlemek,
  4. Çevrimdışı, zaman aşımı ve sunucu hatalarında doğru yönlendirmeyi göstermek,
  5. Global aksiyon gerektiren hataları özellik ekranlarından bağımsız yönetmek,
  6. Teknik ayrıntıları hata takibine gönderirken arayüzde sade bir mesaj göstermek,
  7. Bütün modüllerde aynı kullanım biçimini sağlamak.

Hata yaşam döngüsü

Hata yönetimini tek bir yardımcı fonksiyona sıkıştırmadık. Her katmanın kendi işini yaptığı bir dönüşüm zinciri kurduk:

Dio / HTTP hatası
      ↓
Tip güvenli ağ hatası
      ↓
Backend sonucu veya ortak mesaj eşlemesi
      ↓
ServiceMessageResult
      ↓
Cubit failure durumu
      ↓
Toast, ekran içi hata veya global aksiyon

Ağ katmanı ham exception’ı sınıflandırıyor. Repository sonucu Either olarak özellik katmanına taşıyor. Ortak hata eşleyici teknik hatayı kullanıcı mesajına çeviriyor. Cubit bu mesajı başarısızlık durumuna ekliyor, ekran da mesajın nasıl gösterileceğine karar veriyor.

Arayüzün DioException’ı, HTTP kodunu ya da response gövdesini bilmesi gerekmiyor. Ağ katmanı da hangi ekranda dialog, toast ya da tam sayfa hata gösterileceğine karışmıyor.

Tip güvenli ağ hataları

Ham exception’lar özelliklere doğrudan taşınmıyor; bütün sonuçlar ortak bir ağ hata modeli altında sınıflandırılıyor. Model, HTTP 400 ve 500 ayrımının yanında taşıma ve veri katmanındaki hataları da kapsıyor:

  • 3xx yönlendirme cevapları,
  • 4xx istemci ve iş kuralı cevapları,
  • 5xx sunucu hataları,
  • Bağlantı, gönderme ve yanıt zaman aşımı,
  • Sertifika ve bağlantı hataları,
  • İptal edilen istekler,
  • İnternet bağlantısının bulunmaması,
  • Boş veya beklenmeyen yanıt,
  • Format ve model ayrıştırma hataları,
  • Sınıflandırılamayan beklenmeyen hatalar.

Bir özellik, isterse belirli bir hata türü için kendi davranışını tanımlayabiliyor. Tanımlamazsa ortak varsayılan devreye giriyor. Böylece istisnai ürün ihtiyaçları karşılanırken standart hata yönetiminin dışına çıkılmıyor.

Backend mesajı ile uygulama mesajının dengesi

Backend yanıtları ortak bir servis zarfı içinde sonuç, cevap kodu, mesaj, mesaj türü ve hata durumu taşıyor. Geçerli bir iş mesajı geldiğinde bu bilgi korunuyor. Örneğin seçtiği kaynağın artık kullanılamadığını kullanıcı genel bir hata yerine doğrudan görebiliyor.

Backend mesajı yoksa ya da yanıt güvenli biçimde ayrıştırılamıyorsa uygulamanın yerelleştirilmiş varsayılan mesajları kullanılıyor. Mesajın sonuna eklenen kısa destek kodları, kullanıcıya teknik ayrıntı göstermeden destek ekibinin hata sınıfını ayırt etmesini sağlıyor.

Böylece iki uçtan da uzak durmuş oluyoruz: her durumda “Bir hata oluştu” deyip anlamı kaybetmekten ve backend ya da kütüphane exception’ını olduğu gibi göstererek teknik ayrıntıyı kullanıcıya sızdırmaktan.

Kullanıcıya anlamlı mesaj üretmek

Teknik hata arayüze ulaşmadan önce ortak bir ServiceMessageResult modeline dönüştürülüyor. Bu model metnin yanında mesajın türünü de taşıyor: başarı, bilgi, uyarı ya da hata.

İnternet yoksa kullanıcıya bağlantısını kontrol etmesini söyleyen bir uyarı çıkıyor. Yanıt zaman aşımına uğradığında işlemin beklenen sürede tamamlanamadığı söyleniyor; bağlantı yokmuş gibi davranılmıyor. Sunucu hatasında sorunun hizmet tarafında olduğu anlatılıyor ve kullanıcıya teknik ayrıntı gösterilmiyor. 404 için kaynağın bulunamadığını söyleyen kontrollü bir mesaj kullanılıyor.

Çok fazla istek gönderildiğinde backend mesajı okunabiliyorsa o gösteriliyor, okunamıyorsa bu duruma özel yerel bir mesaj çıkıyor. Model ayrıştırma hatasında üretimde güvenli bir genel mesaj gösterilirken teknik ayrıntı hata takip sistemine gidiyor; geliştirme ortamında ayrıştırmanın nerede koptuğu daha görünür. İsteği kullanıcı iptal ettiyse hiç mesaj gösterilmiyor.

Global ve yerel hata ayrımı

Bazı hatalar yalnızca açık olan özelliği ilgilendiriyor; geçersiz bir indirim kodu sepet ekranının kendi meselesi. Bazıları ise bütün uygulamayı etkiliyor: internet bağlantısının olmaması, sunucuya erişilememesi ya da oturumun geçersiz hale gelmesi gibi.

Ortak hata katmanı global mesajı kendisi gösterdiğinde sonucu “global olarak gösterildi” diye işaretliyor. Özellik ekranındaki mesaj bileşeni bu alana bakıp aynı bildirimi tekrar göstermiyor. Böylece tek bir ağ hatası hem global bir toast, hem ekran hatası, hem de ikinci bir toast üretmiyor.

Yerel hatalarda Cubit mesajı kendi failure durumuna taşıyor. Ekran bunu akışa göre toast, dialog ya da içerik alanında bir hata görünümü olarak sunabiliyor. Metin ortak kalıyor, sunum biçimi ürün bağlamına göre değişebiliyor.

Oturum ve yetki hataları

401 yanıtını ekrana “Yetkisiz” yazarak çözemezsiniz. Kullanıcının aktif oturumu varsa token yenileme akışı çalışıyor ve istek kontrollü biçimde tekrar deneniyor. Yenileme başarısız olursa uygulama mevcut rotayı da hesaba katarak güvenli çıkış sürecini başlatıyor.

Aynı anda birden fazla istek 401 dönerse her birinin ayrı ayrı çıkış işlemi başlatması bir yarış durumuna yol açabilirdi. Bu yüzden oturum sonlandırma kısmı kilit altında çalışıyor ve kullanıcı durumunu tek bir akış temizliyor.

Bazı yetki hataları oturumla ilgili değil; kullanıcının başka biri adına işlem yapma kapsamıyla ilgili. Bu durumda servis mesajı bir dialog içinde gösteriliyor ve kullanıcı geçerli bir başlangıç noktasına yönlendiriliyor. Aynı 4xx ailesi içinde bile ürün davranışı hatanın anlamına göre değişiyor.

Sessizce ele alınması gereken durumlar

Her exception için mesaj göstermek gerekmiyor.

Kullanıcı ekrandan ayrıldığı için iptal edilen bir ağ isteği normal yaşam döngüsünün parçası ve sessizce sonlandırılıyor. Bazı akışlarda artık var olmayan bir kaynak için dönen 410 da kullanıcıya hata göstermeden, mevcut durumu yenileyerek ele alınabiliyor. Global olarak gösterilmiş bir hata da yerel katmanda tekrar açılmıyor.

Bu ayrım arayüzdeki hata gürültüsünü azalttı. Kullanıcı artık yalnızca bir şey yapması ya da beklentisini değiştirmesi gerektiğinde mesaj görüyor.

Yerelleştirme ve destek kodları

İstemcinin ürettiği bütün kullanıcı mesajları çeviri sisteminden geliyor. Bağlantı, zaman aşımı ve genel sunucu mesajları desteklenen bütün dillerde aynı anlamı taşıyor; özellikler kendi içlerinde sabit metin üretmiyor.

Ortak mesajlara eklenen kısa hata kodlarının iki faydası var. Kullanıcı destek ekibine uzun bir teknik açıklama yapmak zorunda kalmıyor, “FL002 kodunu görüyorum” demesi yetebiliyor. Ekip de bu koddan sorunun zaman aşımı mı, bağlantı mı yoksa genel bir istemci hatası mı olduğunu hızla anlayabiliyor.

Geliştirici yetkisi olan kullanıcılar için mesaj bileşeni ağ hatasının ayrıntısını kopyalanabilir biçimde gösterebiliyor. Bu, normal kullanıcı deneyimine ya da üretim mesajlarına yansımıyor.

Özellik modüllerinde kullanım

Randevu, sağlık bilgileri, dosyalar, reçeteler, hatırlatıcılar, mağaza ve cihazlar gibi birçok modül ortak altyapıyı aynı şekilde kullanıyor. Repository çağrısı başarı ya da hata olmak üzere iki açık sonuç üretiyor. Cubit başarıda veriyi, hatada normalize edilmiş kullanıcı mesajını state’e taşıyor.

Kodun kısalması güzel ama asıl fayda kararların tek yerde verilmesi. Yeni bir modül, bağlantı hatasının hangi metne çevrileceğini, 5xx mesajının daha önce gösterilip gösterilmediğini ya da parse hatasının raporlanıp raporlanmayacağını yeniden düşünmek zorunda kalmıyor.

Gözlemlenebilirlik ile bağlantısı

Kullanıcı mesajı üretmek ve teknik hatayı incelemek aynı akışın iki çıktısı olarak tasarlandı. Dio, model ayrıştırma ve beklenmeyen istemci hataları ortak hata facade’ine gönderiliyor. Bu kayıtlar oturum, ekran ve ağ zaman çizelgesiyle birleştirilebildiği için kullanıcı sade bir mesaj görürken teknik ekip ayrıntılı neden analizi yapabiliyor.

Örneğin kullanıcı yalnızca “Sunucuya şu anda ulaşılamıyor” mesajını görüyor. Hata kaydında ise isteğin adresi, süresi, yanıt durumu, aktif ekran, çağrıyı yapan akış ve stack trace bulunabiliyor. İyi bir kullanıcı deneyimi sunmak ile hata ayıklayabilmek arasında seçim yapmak gerekmiyor.

Sonuç

Merkezi hata yönetimi sayesinde ağ katmanındaki teknik çeşitlilik, arayüzde tutarlı bir dille karşılanıyor. Bağlantı kesintisi, zaman aşımı, sunucu sorunu, iş kuralı hatası ve istemci veri hatası artık aynı “bir şeyler ters gitti” mesajının arkasında kaybolmuyor.

Özellik ekipleri HTTP ve Dio ayrıntılarıyla uğraşmadan kendi başarı ve hata durumlarına odaklanabiliyor. Global hata davranışları tek noktadan yönetiliyor, tekrarlanan bildirimler azaldı. Kullanıcıların destek ekibine ilettiği kısa hata kodları da teknik incelemeyi hızlandırıyor.

Öğrendiklerim

  • Her teknik hata bir kullanıcı hatası değil. İptal edilen ya da yaşam döngüsü yüzünden sona eren istekler sessizce ele alınmalı.
  • Mesaj, sorunu kimin çözebileceğini göstermeli. Bağlantı hatasında kullanıcıya, sunucu hatasında sisteme yönelik bir açıklama verilmeli.
  • Backend mesajı değerli ama güvenli bir varsayılan şart. Mesaj ayrıştırılamadığında ürünün dili bozulmamalı.
  • Global ve yerel gösterim ayrılmalı. Aynı hatanın birden fazla bileşende görünmesi yalnızca gürültü yaratıyor.
  • Either tabanlı bir sözleşme, özellik katmanını başarı ve başarısızlığı açıkça ele almaya zorluyor ve hata akışını görünür kılıyor.
  • Teknik ayrıntıyı kullanıcıdan saklamak, geliştirme ekibinden de saklamak anlamına gelmemeli.
  • Kısa ve değişmeyen bir hata kodu, destek ile mühendislik arasında belirsiz ekran görüntülerinden çok daha iyi bir referans oluyor.

Aramak için yazın