Mobil Geliştirme

Offline-First Mobil Uygulama: SQLite ve Senkronizasyon

Fatih Algül
27.03.2026 246 görüntülenme

Offline-First Yaklaşımı Nedir?

Offline-first, mobil uygulama geliştirmede ağ bağlantısının olmadığı durumu varsayılan senaryo olarak kabul eden bir mimari yaklaşımdır. Kullanıcı çevrimdışıyken uygulama tam işlevsel çalışır; bağlantı geldiğinde veriler arka planda sunucuyla senkronize edilir. Bu yaklaşım özellikle Türkiye gibi mobil ağ kalitesinin bölgeden bölgeye değiştiği coğrafyalarda kullanıcı deneyimini ciddi ölçüde iyileştirir.

Geleneksel "online-first" uygulamalarda her işlem sunucuya bağımlıdır. Bağlantı kesildiğinde kullanıcı ya hata mesajı görür ya da uygulamayı kullanamaz hale gelir. Offline-first yaklaşımında ise tüm veriler önce yerel veritabanına yazılır, kullanıcı hiçbir kesinti yaşamaz.

Neden SQLite?

SQLite, offline-first mimarinin temel taşıdır. Hem Android hem iOS platformlarında native olarak desteklenir, sıfır yapılandırma gerektirir ve tek bir dosyada tüm veritabanını barındırır. Performans açısından bakıldığında, yerel disk üzerinde milisaniyeler içinde binlerce kayıt okunabilir ve yazılabilir.

  • Sıfır sunucu bağımlılığı: SQLite tamamen cihaz üzerinde çalışır, herhangi bir arka plan servisi gerektirmez.
  • ACID uyumlu: Transaction desteği sayesinde veri bütünlüğü garanti altındadır.
  • Küçük boyut: Kütüphane boyutu yaklaşık 600 KB'dir, mobil uygulamalar için idealdir.
  • Cross-platform: React Native, Flutter, Kotlin Multiplatform gibi framework'lerde kolayca kullanılabilir.

Veritabanı Şeması Tasarımı

Offline-first bir uygulama için veritabanı şeması tasarlarken senkronizasyon ihtiyaçlarını baştan düşünmek gerekir. Her tabloya senkronizasyon için gerekli meta alanları eklenmelidir:

CREATE TABLE tasks (
    id TEXT PRIMARY KEY,
    title TEXT NOT NULL,
    description TEXT,
    is_completed INTEGER DEFAULT 0,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    sync_status INTEGER DEFAULT 0,
    server_id TEXT,
    deleted_at TEXT
);

Burada kritik alanlar şunlardır:

  • id: İstemci tarafında UUID ile üretilir. Sunucu ID'sine bağımlılık ortadan kalkar.
  • sync_status: 0 = senkronize edilmedi, 1 = senkronize, 2 = senkronizasyon çakışması. Bu alan hangi kayıtların sunucuya gönderilmesi gerektiğini belirlemeye yarar.
  • updated_at: Çakışma çözümlemesinde hangi kaydın daha güncel olduğunu belirlemek için kullanılır.
  • deleted_at: Soft delete yaklaşımı. Kayıtlar fiziksel olarak silinmez, silinme zamanı işaretlenir. Bu sayede silme işlemi de senkronize edilebilir.

Değişiklik Takibi (Change Tracking)

Senkronizasyonun doğru çalışması için yerel değişikliklerin izlenmesi gerekir. Bunun için ayrı bir change log tablosu oluşturmak en sağlıklı yöntemdir:

CREATE TABLE change_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    table_name TEXT NOT NULL,
    record_id TEXT NOT NULL,
    operation TEXT NOT NULL, -- INSERT, UPDATE, DELETE
    changed_fields TEXT,     -- JSON formatında
    timestamp TEXT NOT NULL,
    synced INTEGER DEFAULT 0
);

Her veritabanı yazma işleminde bu tabloya otomatik olarak bir kayıt eklenir. SQLite trigger'ları bu işlemi otomatikleştirmek için mükemmel bir araçtır:

CREATE TRIGGER track_task_update
AFTER UPDATE ON tasks
BEGIN
    INSERT INTO change_log (table_name, record_id, operation, timestamp)
    VALUES ('tasks', NEW.id, 'UPDATE', datetime('now'));
    UPDATE tasks SET sync_status = 0 WHERE id = NEW.id;
END;

Senkronizasyon Stratejileri

1. Timestamp Tabanlı Senkronizasyon

En basit yaklaşımdır. İstemci, son senkronizasyon zamanını saklar ve sunucudan bu zamandan sonra değişen kayıtları ister. Uygulaması kolaydır ancak saat farklılıkları ve aynı anda yapılan değişikliklerde sorun çıkarabilir.

// Son senkronizasyon zamanından sonra değişen yerel kayıtları al
SELECT * FROM tasks 
WHERE sync_status = 0 
ORDER BY updated_at ASC;

// Sunucudan güncellemeleri çek
GET /api/tasks?since=2026-03-28T10:30:00Z

2. Versiyon Vektörü (Version Vector)

Her cihaz kendi sayacını tutar. Senkronizasyon sırasında cihazlar birbirlerinin versiyon vektörlerini karşılaştırarak hangi değişikliklerin eksik olduğunu belirler. Dağıtık sistemlerde daha güvenilirdir ancak implementasyonu daha karmaşıktır.

3. CRDT (Conflict-free Replicated Data Types)

Çakışma çözümlemesini veri yapısı seviyesinde otomatikleştiren bir yaklaşımdır. Özellikle birden fazla cihazda aynı anda düzenleme yapılan senaryolarda güçlüdür. Ancak her veri tipi için uygun CRDT yapısını seçmek uzmanlık gerektirir.

Çakışma Çözümleme (Conflict Resolution)

Offline-first uygulamaların en zorlu kısmı çakışma yönetimidir. Aynı kayıt birden fazla cihazda çevrimdışıyken düzenlendiğinde bir çakışma oluşur. Yaygın çözüm stratejileri şunlardır:

  • Last Write Wins (LWW): En son güncellenen kayıt kazanır. Basit ama veri kaybına yol açabilir.
  • Alan bazlı birleştirme (Field-level merge): Farklı alanlar değişmişse her ikisi de korunur. Aynı alan değişmişse LWW uygulanır.
  • Kullanıcıya sorma: Çakışma tespit edildiğinde kullanıcıya her iki versiyon gösterilir ve seçim yaptırılır.

Pratikte alan bazlı birleştirme en dengeli yaklaşımdır. Aşağıda basit bir implementasyon örneği verilmiştir:

function resolveConflict(localRecord, serverRecord) {
    const merged = { ...localRecord };
    const localTime = new Date(localRecord.updated_at);
    const serverTime = new Date(serverRecord.updated_at);

    for (const field of Object.keys(serverRecord)) {
        if (field === 'id' || field === 'sync_status') continue;

        if (localRecord[field] !== serverRecord[field]) {
            // Farklı alanlar değişmişse sunucu versiyonunu al
            // Aynı alan değişmişse en güncel olanı al
            merged[field] = serverTime > localTime 
                ? serverRecord[field] 
                : localRecord[field];
        }
    }
    return merged;
}

Senkronizasyon Döngüsünü Tetikleme

Senkronizasyon işleminin ne zaman tetikleneceği de önemli bir tasarım kararıdır. Birden fazla strateji birlikte kullanılmalıdır:

  1. Bağlantı geri geldiğinde: Cihaz çevrimiçi olduğunda bekleyen tüm değişiklikler gönderilir.
  2. Periyodik: Belirli aralıklarla (örneğin 5 dakikada bir) arka planda senkronizasyon çalıştırılır.
  3. Push notification ile: Sunucu tarafında kritik bir değişiklik olduğunda istemciye bildirim gönderilir ve senkronizasyon tetiklenir.
  4. Kullanıcı tetiklemesiyle: Pull-to-refresh gibi kullanıcı eylemleri anında senkronizasyon başlatır.

Pratik İpuçları ve Dikkat Edilmesi Gerekenler

  • Kuyruklama mekanizması kullanın: Senkronizasyon isteklerini bir kuyrukta toplayın. Ağ hatası durumunda üstel geri çekilme (exponential backoff) ile tekrar deneyin.
  • Batch işlem yapın: Her değişikliği ayrı ayrı göndermek yerine biriktirip toplu olarak sunucuya gönderin. Bu hem bant genişliğinden tasarruf sağlar hem de sunucu yükünü azaltır.
  • Veri boyutunu kontrol edin: Yerel veritabanında gereksiz veri birikmesini önleyin. Senkronize edilmiş change log kayıtlarını belirli aralıklarla temizleyin.
  • Migration stratejisi planlayın: Uygulama güncellemelerinde veritabanı şeması değişebilir. SQLite migration'larını versiyonlu bir şekilde yönetin.
  • Test edin: Uçak modu, yavaş ağ, ağ kesilmesi gibi senaryoları mutlaka test edin. Charles Proxy veya Android Studio'nun Network Profiler aracı bu testlerde çok faydalıdır.

Popüler Araçlar ve Kütüphaneler

Sıfırdan her şeyi yazmak yerine mevcut çözümlerden faydalanabilirsiniz:

  • WatermelonDB: React Native için optimize edilmiş, lazy loading destekli offline-first veritabanı. SQLite üzerine inşa edilmiştir ve senkronizasyon protokolü dahili olarak gelir.
  • Realm (Atlas Device Sync): MongoDB tarafından desteklenen, otomatik çakışma çözümlemesi sunan bir çözüm.
  • PowerSync: SQLite tabanlı, Postgres ile senkronize çalışan açık kaynak bir araç.
  • Drift (eski adıyla Moor): Flutter için reactive SQLite wrapper. Senkronizasyon katmanını kendiniz eklemeniz gerekir ancak yerel veritabanı yönetimi için çok güçlüdür.
  • ElectricSQL: Postgres ile SQLite arasında otomatik senkronizasyon sağlayan yeni nesil bir araç.

Sonuç

Offline-first mimari, başlangıçta ek karmaşıklık getirmiş gibi görünse de uzun vadede daha güvenilir, hızlı ve kullanıcı dostu uygulamalar ortaya çıkarır. SQLite'ın sağlamlığı ve hafifliği bu mimarinin temelini oluştururken, doğru senkronizasyon stratejisi ve çakışma çözümleme mekanizması uygulamanın başarısını belirler. Projenizin gereksinimlerini analiz edin, uygun stratejiyi seçin ve mümkünse kanıtlanmış kütüphanelerden faydalanarak geliştirme sürecinizi hızlandırın.

Yazar Hakkında
Fatih Algül
TechSoft Solutions
Proje mi var?

Yazılım, IoT veya otomasyon konularında destek almak ister misiniz?

İletişime Geç