İçeriğe geç
wedevit

8 Ekim 2026 · 10 dk okuma · yazılım

İlhan Buğra Aslan

Sahada sinyal yok, uygulama duruyor: çevrimdışı çalışan uygulama nasıl yazılır?


Çevrimdışı çalışan bir uygulamanın zor kısmı veriyi cihazda tutmak değil, o veriyi sonradan geri birleştirmek. Çalışan tasarımın beş kararı var: ekran sunucudan değil cihazdaki veritabanından okur, her yazma işlemi doğrudan servise değil cihazdaki bir giden kuyruğuna düşer, kayıtların kimliğini sunucu değil cihaz üretir, çakışma kuralı kayıt bazında değil alan bazında yazılır ve cihaza veritabanının tamamı değil o kullanıcının işine yarayan dilim iner. Bu kararlar verilmeden eklenen "çevrimdışı desteği" demoda çalışır, sahada üçüncü haftada veri kaybettirir. Aşağıda her birinin pratikte nerede çatladığı var.

Önce ayırın: çevrimdışı okuma mı, çevrimdışı yazma mı?

Bu iki gereksinim arasındaki maliyet farkı kat kat. Teknisyen sahada iş emrini, müşteri adresini ve geçmiş servis kayıtlarını görebilsin yeterliyse iş bir önbellek işidir: veriyi önceden indirip cihazda tutarsınız, ekran onu okur, bitti. Çakışma yok, kuyruk yok, birleştirme yok.

Aynı teknisyen çevrimdışıyken iş emrini kapatacak, parça kullanımı girecek ve müşteriye imza attıracaksa farklı bir sisteme bakıyorsunuz. Artık iki cihaz aynı kaydı farklı yönlere çekebilir ve bunun bir kuralı olmak zorunda.

Şartname toplantısında sorulacak soru şu: hangi ekranlar sadece okunacak, hangilerine çevrimdışı veri girilecek? Çoğu projede ikinci listenin üç ekrandan ibaret olduğu ortaya çıkar. O üç ekranı doğru yazmak, her şeyi çevrimdışı yazılabilir yapmaya çalışmaktan hem ucuz hem sağlamdır. Bu ayrımın yazılım şartnamesinde net durması gerekir, çünkü teklif farkının büyük kısmı burada saklıdır.

Ekran ağdan değil, cihazdaki veritabanından okur

Çevrimdışı mimarinin tek cümlelik özeti bu. Arayüz hiçbir zaman doğrudan servise sormaz; cihazdaki veritabanını okur. Senkron ise arka planda çalışan ayrı bir iştir, o veritabanını günceller, arayüz değişikliği görür.

Bu tersine çevirmenin iki faydası var. Birincisi, ağ varken de yokken de kodun tek bir yolu olur, "internet varsa şuradan, yoksa buradan" ikiliği ortadan kalkar. İkincisi, uygulama ağ iyiyken bile hızlanır, çünkü liste ekranı yerel sorguyla açılır.

Cihaz tarafında pratikte SQLite kullanılır: Android'de Room, iOS'ta Core Data veya SwiftData, React Native ve Expo tarafında yerel SQLite paketleri, Flutter'da Drift. Tarayıcıda karşılığı IndexedDB'dir, fakat tarayıcının kendine has sorunları var.

Tarayıcıda çevrimdışı, uygulamadakinden daha kırılgan

PWA çoğu iş için yeterli bir cevap, ama "cihaz günlerce internetsiz kalacak" senaryosunda tarayıcının iki sert sınırına çarparsınız.

Birincisi veri ömrü. WebKit'in 2020'de duyurduğu kurala göre Safari, kullanıcı siteyle etkileşmediği yedi günlük Safari kullanımının ardından sitenin betikle yazılabilen tüm depolamasını siler: IndexedDB, LocalStorage, SessionStorage, media keys, Service Worker kayıtları ve önbellek. Yani cuma günü sahada toplanan veri, uygulama iki hafta açılmazsa gitmiş olabilir. Aynı duyuruda istisna da yazıyor: ana ekrana eklenen web uygulamaları Safari'nin parçası sayılmaz ve kendi kullanım sayaçlarıyla çalışır. Pratik sonucu şu, tarayıcı sekmesinde bırakılan bir saha uygulaması güvenli değildir; kullanıcıya kurulumu yaptırmak gereksinimin parçasıdır.

İkincisi arka plan senkronu. Background Sync API Chrome, Edge, Opera ve Samsung Internet tarafında var; Firefox ve Safari'de yok, iOS tarafında hiç yok. Workbox gibi kütüphaneler desteklenmeyen tarayıcılarda geri düşüş uygular ve bekleyen istekleri Service Worker her uyandığında yeniden dener, ama bu tarayıcının kendi zamanlamasıyla aynı şey değil. Uygulamanın yeniden açılması gerekir.

Karar basitleşiyor: günde birkaç saatlik kopukluk için yüklenmiş bir PWA iş görür, günlerce kopukluk ve arka planda garantili gönderim isteniyorsa yerel uygulama tarafına geçersiniz. Bu kararın diğer boyutları native mi cross-platform mı yazısında duruyor.

Kimliği sunucu değil, cihaz üretsin

Çevrimdışı oluşturulan kayıt, kimliğini sunucudan isteyemez. Otomatik artan tamsayı anahtar kullanırsanız iki cihaz da kendi yerel veritabanında 47 numarayı üretir ve senkronda bu iki kayıt birbirine karışır.

Doğru cevap kimliği cihazda üretmek. UUID bunun için var. Rastgele UUID (v4) çalışır; RFC 9562 ile standartlaşan zaman sıralı UUID (v7) ise aynı benzersizliği verirken sunucu tarafındaki indekslerde daha iyi davranır, çünkü değerler kronolojik sırayla büyür.

Bunun ikinci faydası kimlikten bağımsız. Cihazda üretilen kimlik aynı zamanda idempotency anahtarı olur: kayıt gönderildi ama cevap kaybolduysa, istemci yeniden denediğinde sunucu aynı anahtarı görüp ikinci kaydı oluşturmaz. Ağ üzerinden teslimin neden en iyi ihtimalle "en az bir kez" çalıştığını ve idempotent işlemenin nasıl kurulduğunu veri senkronizasyonu yazısında ayrıntılı anlatmıştık.

Üçüncüsü de pratikte en çok işe yarayanı: cihazda üretilen kimlik, kayıt daha sunucuyu görmeden ona referans verilebilmesini sağlar. Teknisyen çevrimdışıyken yeni bir müşteri açıp hemen altına iş emri giriyorsa, iş emrinin işaret ettiği müşteri kimliği zaten bellidir.

Değişiklikler kuyruğa yazılır, ekran kuyruğu beklemez

Çevrimdışı yazma işini taşıyan yapı bir giden kutusudur. Kullanıcı kaydet dediğinde iki şey aynı anda ve aynı işlem içinde olur: veri yerel tabloya yazılır ve değişiklik kuyruk tablosuna bir satır olarak düşer. Ekran o anda güncel görünür, kullanıcı yoluna devam eder.

Kuyruk satırının taşıdığı alanlar az ve belli: değişiklik kimliği, hangi varlık, hangi işlem (oluştur, güncelle, sil), gövde, oluşturulma zamanı, deneme sayısı ve durum. Üç kural bu tabloyu ayakta tutar.

Aynı kaydın değişiklikleri üretildikleri sırayla gönderilir. Teknisyen iş emrini önce açıp sonra kapattıysa sunucu da bunu bu sırayla görmeli, aksi halde kapanmış bir kayıt yeniden açılır.

Başarısız denemeler artan bekleme süresiyle tekrarlanır ve bir noktada durur. Sonsuza kadar deneyen bir kuyruk, bozuk bir kaydı taşıyorsa pili bitirir ve logu doldurur.

Kuyruk kalıcı depolamada tutulur. Bellekteki bir dizi, uygulama işletim sistemi tarafından kapatıldığında yok olur ve kullanıcı o veriyi girdiğini sanmaya devam eder. Aynı dayanıklılık mantığının sunucu tarafındaki karşılığı arka plan işleri ve kuyruk mimarisi yazısında.

Çakışma kuralını kayıt bazında değil alan bazında seçin

Varsayılan davranış genelde şudur: son yazan kazanır. Kayıt bazında uygulandığında bu kural sessizce veri siler. Depo sorumlusu çevrimdışıyken adet alanını düzeltti, ofisteki kişi aynı dakikada aynı kaydın notunu güncelledi. İki güncelleme de kaydın tamamını taşıyorsa, sonradan gelen diğerinin düzeltmesini siler ve kimse fark etmez.

Pratik çözüm kuralı alan bazına indirmek. Her alan kendi son değişiklik damgasını taşır, birleştirme alan alan yapılır. Böylece adet alanını depo, not alanını ofis günceller ve ikisi de kalır.

Alan tipi başına da karar vermek gerekir. Durum, seçim ve ayar alanları için son yazan kazanır kuralı doğrudur. Sayaçlar için değildir: iki cihaz da stoktan 3 düştüyse sonuç eksi 6 olmalı, eksi 3 değil. Bu yüzden sayaçlarda mutlak değer değil değişim gönderilir ya da hesap sunucuda yeniden yapılır. Serbest metin ve not alanları için çoğu zaman en doğrusu üzerine yazmak değil eklemektir; iki not da kalır, kullanıcı hangisinin geçerli olduğuna bakar.

Bazı çakışmalar teknik değil ticari sorudur. İki teknisyen aynı işi kendi üzerine kapattıysa, hiçbir birleştirme algoritması doğru cevabı bilmez. Bu tür kayıtlar için insana düşen bir ekran gerekir: çakışan kayıtlar listesi, iki sürüm yan yana, kim neyi ne zaman yazdı. Bu ekranı yazmayan projeler çakışmayı sessizce bir tarafa yazar ve altı ay sonra kimse rakamlara güvenmez.

Bir uyarı da saatler üzerine. Telefon saatleri kayar, kullanıcılar elle değiştirir. Beş dakika ileri kurulmuş bir cihaz, o beş dakika boyunca herkesin üstüne yazar. Zaman damgasını duvar saatiyle birlikte geri gitmeyen bir sayaçtan üreten hibrit mantıksal saat (HLC) bu sorunu çözer, modern senkron kütüphanelerinin çoğu bunu zaten içeride kullanır. Metin üzerinde gerçek eşzamanlı düzenleme gerekiyorsa konu CRDT kütüphanelerine (Automerge, Yjs, Loro) gider; saha uygulamalarının büyük kısmı buna ihtiyaç duymaz.

Cihaza veritabanının tamamını indirmeyin

İlk senkronda her şeyi indiren bir uygulama, sahada zayıf hatta ilk açılışta on dakika bekletir. Kapsamı baştan daraltın: kullanıcıya atanmış işler, kendi bölgesindeki müşteriler, son doksan günün kayıtları.

Bu işi hazır çözümler bir yapılandırma dosyasına dönüştürüyor. PowerSync örneğinde senkron kuralları YAML dosyasında SQL benzeri sorgularla yazılır; servis veriyi parametre değerine göre kovalara ayırır (örneğin her kullanıcı kimliği için bir kova) ve istemci bağlandığında yalnızca kendi kovalarını indirir, veri cihazdaki SQLite veritabanında durur. Kendi senkronunuzu yazsanız bile mantık aynıdır: kimin neyi göreceğini veri modelinden türetilen bir kural belirler.

Sonraki senkronlar değişiklik imleciyle çalışır. İstemci gördüğü son damgayı gönderir, sunucu yalnızca o damgadan sonra değişenleri döner. İki ayrıntı burada sık atlanır. Birincisi, silinen kayıtlar için mezar taşı tutulmazsa cihaz o kaydı asla silmez ve sahadaki liste gerçekle uyuşmaz. İkincisi, imleç olarak yalnızca zaman damgası kullanmak aynı milisaniyedeki kayıtların atlanmasına yol açar; damga ve kimlik ikilisi üzerinden sayfalamak gerekir.

Hazır bir senkron servisi seçecekseniz tedarikçi riskini de hesaba katın. MongoDB, Eylül 2024'te Atlas Device SDK ve Atlas Device Sync'in kullanımdan kaldırılacağını duyurdu ve servisi 30 Eylül 2025'te sonlandırdı; yerel veritabanı açık kaynak olarak kaldı, bulut senkronu kalmadı. Üstüne saha uygulaması kurmuş ekipler bir yıl içinde göç etmek zorunda kaldı. Seçerken sorulacak soru şu: bu servis kapanırsa senkron protokolünü kendimiz işletebilir miyiz?

Arka plan senkronu için ne söz verebilirsiniz?

İş tarafı genelde "uygulama kapalıyken de senkron olsun" ister. Verilebilecek söz işletim sisteminin izin verdiği kadardır.

Android'de WorkManager işi dayanıklı biçimde planlar, cihaz yeniden başlasa bile kaydı korur ve ağ bağlantısı, şarj, pil durumu gibi koşullara bağlanabilir. Buna karşılık Doze modu devreye girdiğinde (cihaz prizden çıkmış, hareketsiz ve ekran bir süredir kapalıysa) ağ erişimi askıya alınır ve bekleyen işler ertelenir. İş çalışır, ama "beş dakika içinde" çalışmaz.

iOS'ta zamanlama daha da sistemin elinde. Arka plan tazeleme görevleri kısa sürelidir ve ne zaman çalışacağına işletim sistemi kullanım alışkanlığına bakarak karar verir. Büyük yüklemeleri bu görevlerin içine sığdırmaya çalışmak yerine arka plan URLSession'ına devretmek gerekir; aktarımı uygulama değil sistem yürütür.

Buradan çıkan gerçekçi taahhüt şudur: veri cihazda kaybolmaz, uygulama açıkken ve ağ geldiğinde gönderilir, işletim sistemi izin verirse arka planda da gönderilir. Vardiya sonunda kuyruğun boşaldığını görmek operasyonun sorumluluğundadır ve bunu görebileceği bir ekran olmalıdır.

Fotoğraflar senkronun en ağır yükü

Saha uygulamalarında veri trafiğinin büyük kısmı metin değil fotoğraftır. Hasar tespiti, montaj öncesi ve sonrası, teslim imzası, sayaç görüntüsü. On iki megapiksel bir fotoğraf birkaç megabayt tutar ve bir günlük ziyaret listesi kolayca yüz megabayta çıkar.

Üç basit kural yükü yönetilebilir tutar. Fotoğrafı kaydın gövdesinin içine gömmeyin; dosya cihazda dursun, kuyruk satırı yalnızca dosyanın yolunu taşısın. Önce kaydı gönderin, dosyayı ayrıca yükleyin, böylece zayıf hatta yarım kalan bir yükleme iş emrinin kapanmasını engellemez. Çekim anında boyutu düşürün, çünkü sigorta eksperi de teknik servis de iki megapiksel görüntüyle çalışabilir.

Yükleme tarafının kaldığı yerden devam edebilmesi, doğrulama ve saklama kuralları ayrı bir konu; o kısmı dosya yükleme özelliği yazısında ele almıştık.

Kullanıcı kuyruğu görebilmeli

Çevrimdışı uygulamaların en sık şikayeti veri kaybı değil, veri kaybı şüphesidir. Kullanıcı gönderip gönderemediğini bilmiyorsa aynı kaydı ikinci kez girer.

Üç gösterge bunu çözer: bekleyen kayıt sayısı, son başarılı senkron zamanı ve başarısız kayıtların listesi. Üçüncüsü en çok ihmal edileni. Yirmi kez denenip reddedilmiş bir kayıt artık teknik bir problem değildir, birinin bakması gerekir; sunucu neden reddettiğini (zorunlu alan boş, kayıt başkası tarafından silinmiş, yetki yok) kullanıcının anlayacağı dille söylemelidir.

Liste ekranında her satırın senkron durumunu küçük bir işaretle göstermek, destek hattına gelen çağrıların önemli bir kısmını kesiyor.

Cihazda duran veri bir güvenlik kararıdır

Çevrimdışı çalışmak, şirket verisinin kilitli sunucudan çıkıp çantada dolaşması demektir. Üç şeye bakın.

Veriyi azaltın. Cihaz yalnızca o kullanıcının işine yarayan dilimi indiriyorsa, telefon kaybolduğunda ortaya çıkan kayıp da o dilim kadardır. Tüm müşteri listesini her cihaza indiren bir uygulama, her çalışanın cebinde bir veri ihracı taşır.

Depolamayı şifreleyin. İşletim sisteminin cihaz şifrelemesi temel katmandır, hassas veri için veritabanı seviyesinde şifreleme (örneğin SQLCipher) eklenebilir; oturum anahtarları ise dosyaya değil Keychain veya Android Keystore'a yazılır.

Çevrimdışı oturum süresini açıkça kararlaştırın. Erişim anahtarı bir saatte dolarken kullanıcı üç gün internetsiz kalacaksa, uygulamanın çevrimdışı ne kadar süre çalışmaya devam edeceğini ve o süre dolduğunda ne olacağını baştan yazmanız gerekir. Kayıp cihazın uzaktan silinmesi ayrı bir konu; kişisel telefonlarda bunun sınırlarını BYOD ve mobil cihaz yönetimi yazısında anlatmıştık.

Uçak modu test değildir

Çevrimdışı hataların çoğu "ağ yok" halinde değil, ağın kötü olduğu halde ortaya çıkar. Test senaryolarınızda şunlar olmalı: yüklemenin yarısında kopan bağlantı, çok yavaş ama ölü olmayan hat, uygulamanın senkron sırasında işletim sistemi tarafından kapatılması, saati beş dakika ileri kurulmuş cihaz, aynı kaydı aynı anda değiştiren iki cihaz ve otuz gün çevrimdışı kaldıktan sonra tek seferde bağlanan bir cihaz.

Son senaryo en çok sürpriz çıkaranı, çünkü biriken kuyruk hem büyük hem de kendi içinde sıralı olmak zorunda. Bu testleri gerçekçi hacimli veriyle yapmak gerekir; nasıl üretileceği test verisi yönetimi yazısının konusu.

Başlamadan önce cevaplayın

  1. Hangi ekranlar çevrimdışı okunacak, hangilerine çevrimdışı veri girilecek? İkinci liste kaç ekran?
  2. Kullanıcı en fazla ne kadar süre kopuk kalabilir: bir vardiya mı, bir hafta mı? Cevap tarayıcı mı yerel uygulama mı sorusunu da belirler.
  3. Kayıtların kimliğini kim üretiyor? Otomatik artan tamsayıysa çevrimdışı oluşturma daha başlamadan bozuktur.
  4. Aynı kaydı iki kişi değiştirdiğinde hangi alanda kim kazanır ve hangi durumda insan karar verir?
  5. Cihaza veritabanının hangi dilimi iniyor ve ilk senkron kaç megabayt?
  6. Bekleyen ve başarısız kayıtları kullanıcı nerede görüyor?

Bu sorulardan birkaçının cevabı net değilse ya da sahadaki ekip "girdim ama gitmemiş" diyorsa, mevcut senkron akışını birlikte çıkarıp nerede veri kaybettiğini somut olarak gösterebiliriz.


Bu konuda yardıma mı ihtiyacınız var?

iletişime geç →← tüm yazılar