Dış servisten null dönmesiyle bağlantının zaman aşımına uğraması aynı sonuç değildir. İkisini de boş nesneye çevirirsem ekran “kayıt yok” diyebilir; oysa veri kaynağına hiç ulaşamamışımdır. Servis entegrasyonunda ilk işim, sağlayıcının davranışını uygulamanın anlayacağı sonuçlara çevirmektir.
Adaptörün işi her hatayı yutmak değil
Arşivdeki servis yardımcısı yaklaşımını göstermek için küçük bir Result<T> kaydı kullanıyorum. Kod .NET 10 konsolunda çalışır; gerçek HTTP istemcisi veya WCF proxy'si gerektirmez. Amacı başarı, boş sonuç ve bilinen timeout ayrımını görünür kılmak.
static Result<T> Translate<T>(Func<T?> call) where T:class
{
try { var value=call(); return value is null ? new("empty",null) : new("ok",value); }
catch(TimeoutException) { return new("timeout",null); }
}
Console.WriteLine(Translate<string>(()=>null).Status);
Console.WriteLine(Translate<string>(()=>throw new TimeoutException()).Status);
record Result<T>(string Status,T? Value);
İlk çağrı empty, ikinci çağrı timeout yazar. Başarılı çağrı ok ve değer taşır. Yalnız TimeoutException yakalandığına dikkat et: başka bir exception otomatik “servis boş döndü” sonucuna dönüşmez. Bu sınır, beklenen entegrasyon arızasını yönetmekle yazılım hatasını gizlemek arasındaki farktır.
Boş sonuç hangi iş anlamına geliyor?
Bir arama listesinin boş olması başarılı bir yanıttır. Tek bir kaydı kimliğiyle getiren işlemde bulunamama ayrı bir sonuç olabilir. Sağlayıcının null dönmesi de sözleşmeye göre “yok”, “eksik yanıt” veya “yanlış ayrıştırma” anlamına gelebilir. Bu kararı transport yardımcısının her metoda tek bir 404 atamasıyla vermem; kullanım senaryosu karar vermeli.
Üretimde string durumların serbestçe çoğalmasını istemiyorsam kapalı bir durum kümesi veya ortak ServiceResult sözleşmesi kurarım. Başarı verisi, kullanıcı mesajı, alan hataları ve uygulama hata kodu ayrı alanlar olabilir. Controller iş sonucunu HTTP'ye çevirir; iş servisi HttpResponseMessage veya IActionResult taşımak zorunda değildir.
Gerçek istemcide exception türünü ölçmek
Bu örneğin TimeoutException seçimi öğretici bir sadeleştirmedir. HttpClient, sağlayıcı SDK'sı ve WCF aynı arızayı aynı exception türüyle bildirmeyebilir. Async çağrıda iptal ve timeout da birbirine benzeyen hatalarla gelebilir. Çağıranın CancellationToken değerini kontrol ederek kullanıcının iptalini uzak servisin yavaşlığından ayırmak gerekir.
Timeout bütçesini bütün isteğe göre belirlerim. Art arda üç dış çağrının her birine büyük süre vermek, toplam işlem süresini farkında olmadan büyütür. Bağlantı kurma, yanıt alma ve gerekirse yeniden deneme sürelerinin toplamı, kullanıcının beklediği işlem bütçesine sığmalı.
Yeniden deneme hangi işlemi tekrar yapıyor?
Bir okuma isteğini tekrar denemekle ödeme veya başvuru oluşturan bir komutu tekrar göndermek aynı riskte değildir. Timeout anında karşı taraf işlemi tamamlamış olabilir. “Yanıt gelmedi” bilgisini “işlem yapılmadı” diye yorumlayıp otomatik tekrar eklemem. Yazma operasyonlarında idempotency anahtarı veya sorgulanabilir işlem kimliği gibi bir sözleşme gerekir.
Beklenmeyen bir hata uygulamanın merkezi exception sınırına ulaşabilir. Kullanıcıya iç exception mesajı yerine güvenli ve eyleme dönük bir cümle gösterilir; tanı için correlation ID yeterli bir başlangıçtır. Token, tam istek gövdesi veya kişisel kayıtları hata mesajına taşımak sorunu çözmez.
HTTP durumunu semantik belirlesin
Başarılı boş listeyi 200, bulunamayan tek kaydı 404, doğrulamayı 400 ve eşzamanlılık çakışmasını 409 olarak ifade etmek mümkündür. Dış servis arızasının hangi gateway veya kullanılabilirlik koduna karşılık geleceği uygulamanın sözleşmesinde sabit olmalı. 401 ve 403 ise farklıdır: oturum yokluğu ile mevcut kullanıcının yetkisizliği aynı login döngüsüne bağlanmamalı.
Testte dolu sonuç, null, timeout, çağıran iptali ve beklenmeyen exception senaryolarını ayrı kontrol ederim. En önemli doğrulama, başarısız çağrının ekranda başarı bildirimi oluşturmaması ve gerekiyorsa kullanıcının düzenlediği veriyi korumasıdır.