DEV Community

Nuh Mehmet Demirkol
Nuh Mehmet Demirkol

Posted on Originally published at nmdemirkol.com

Offline-First Mobil Mimari: Isar, SQLite ve REST API ile Kusursuz Senkronizasyon

Offline-First Mobil Mimari: Isar, SQLite ve REST API ile Kusursuz Senkronizasyon

Günümüz mobil uygulama ekosisteminde kullanıcı deneyimini (UX) belirleyen en kritik unsurlardan biri, uygulamanın ağ bağlantısından bağımsız olarak ne kadar kararlı çalıştığıdır. Tünelde giden bir metroda, sinyalin zayıf olduğu kırsal bir bölgede ya da uçak modunda bile uygulamanızın çökmeden, takılmadan ve veri kaybetmeden çalışması gerekir.

Bu makalede, modern mobil uygulamalarda Offline-First (Önce Çevrimdışı) mimariyi kurmanın teorik temellerini, yerel veri tabanı (Local DB) seçim kriterlerini ve yerel verileri bir REST API ile kusursuz bir şekilde senkronize etmenin mühendislik pratiklerini ele alacağız.


1. Offline-First Mimari Nedir ve Neden Önemlidir?

Offline-First, uygulamanın birincil veri kaynağı (Single Source of Truth) olarak yerel veri tabanını (Local Database) kabul ettiği bir tasarım örüntüsüdür. Uygulama ağ isteklerinin sonucunu beklemek yerine, doğrudan cihaz üzerindeki yerel veriyi okur ve yazar. Arka planda çalışan bir Senkronizasyon Motoru (Sync Engine), bu yerel verileri uzak sunucuyla (Cloud/REST API) asenkron olarak eşitler.

Avantajları:

  • Ultra Düşük Gecikme (Low Latency): UI, ağ gecikmesine (network latency) takılmadan yerel veri tabanından anında beslenir.
  • Kesintisiz Çalışabilirlik: İnternet bağlantısı koptuğunda dahi tam fonksiyonellik sunulur.
  • Daha Az Pil ve Veri Tüketimi: Ağ istekleri optimize edilerek paketler halinde (batching) gönderilir.

2. Mimari Tasarım Şeması

Kusursuz bir Offline-First mimari, katmanlı bir yapıda tasarlanmalıdır. İş mantığı (Business Logic), veri tabanı motorundan ve ağ katmanından tamamen izole edilmelidir.

+-------------------------------------------------------------+
| UI Katmanı |
+-------------------------------------------------------------+
│ (Reactive Streams / Streams)
▼
+-------------------------------------------------------------+
| Repository Katmanı |
| (Verinin nereden geleceğini yönetir) |
+-------------------------------------------------------------+
│ │
▼ (Okuma / Yazma) ▼ (Tetikleme)
+-------------------+ +-----------------+
| Local Data Source| | Sync Engine |
| (Isar / SQLite) | | (Senkronizasyon)|
+-------------------+ +-----------------+
│
▼ (REST API - JSON)
+-----------------+
|Remote Data Source|
+-----------------+


3. Yerel Veri Tabanı Seçimi: SQLite mı, Isar mı?

Özellik SQLite (Drift / Moor) Isar (NoSQL)
Tür İlişkisel (SQL) Doküman / Nesne Tabanlı (NoSQL)
Hız Orta / İyi Çok Hızlı (Asenkron & Çoklu İzlek)
Reaktivite Stream desteği var (Üçüncü parti kütüphanelerle) Doğal reaktif (Query Watchers)
Şema Göçü (Migration) Manuel SQL sorguları ile zorlayıcı Otomatik şema yönetimi ile kolay

Öneri: Eğer uygulamanız karmaşık çoklu tablolara ve ilişkisel ACID işlemlerine yoğun şekilde ihtiyaç duyuyorsa SQLite (Drift); performans, reaktivite, kolay geliştirme ve Flutter ile mükemmel uyum arıyorsanız modern Isar Database tercih edilmelidir.


4. Senkronizasyon Stratejileri ve Veri Modeli Tasarımı

Verilerin senkronize edilebilmesi için veri modellerimizde bazı meta-verilere (meta-fields) ihtiyacımız vardır. Her tablo/doküman şu alanları barındırmalıdır:

dart
abstract class SyncableEntity {
String get id; // Evrensel tekil kimlik (UUID v4)
DateTime get lastUpdatedAt; // Son güncellenme zamanı
bool get isDirty; // Sunucuya gönderilmeyi bekleyen yerel değişiklik var mı?
bool get isDeleted; // Soft-delete (Yumuşak silme) bayrağı
}

Kritik Tasarım Örüntüsü: Outbox (Giden Kutusu) Tasarımı

Kullanıcı çevrimdışıyken bir işlem gerçekleştirdiğinde, bu işlem doğrudan sunucuya gönderilemediği için bir Outbox tablosuna kuyruğa alınır.

dart
import 'package:isar/isar.dart';

part 'outbox_action.g.dart';

@collection
class OutboxAction {
Id? id = Isar.autoIncrement;

@Index(unique: true)
late String entityId; // Değişen kaydın ID'si

late String entityName; // 'Task', 'User', 'Order' vb.

@enumerated
late ActionType actionType; // CREATE, UPDATE, DELETE

late String payload; // Değişikliğin JSON hali

late DateTime createdAt;
}

enum ActionType { create, update, delete }


5. Senkronizasyon Algoritması (Sync Engine)

Senkronizasyon motoru iki yönlü çalışır: Upstream (Yerelden Sunucuya) ve Downstream (Sunucudan Yerelle).

Adım 1: Upstream (Yerel Değişikliklerin Sunucuya İletilmesi)

dart
class SyncEngine {
final Isar _localDb;
final ApiClient _apiClient;

SyncEngine(this._localDb, this._apiClient);

Future syncUpstream() async {
// 1. Bekleyen yerel işlemleri oku
final pendingActions = await _localDb.outboxActions
.where()
.sortByCreatedAt()
.findAll();

for (var action in pendingActions) {
  try {
    bool isSuccess = false;

    switch (action.actionType) {
      case ActionType.create:
        isSuccess = await _apiClient.post(
          '/api/${action.entityName.toLowerCase()}',
          action.payload,
        );
        break;
      case ActionType.update:
        isSuccess = await _apiClient.put(
          '/api/${action.entityName.toLowerCase()}/${action.entityId}',
          action.payload,
        );
        break;
      case ActionType.delete:
        isSuccess = await _apiClient.delete(
          '/api/${action.entityName.toLowerCase()}/${action.entityId}',
        );
        break;
    }

    if (isSuccess) {
      // İşlem başarılı, kuyruktan sil
      await _localDb.writeTxn(() async {
        await _localDb.outboxActions.delete(action.id!);
      });
    }
  } catch (e) {
    // Hata durumunda loglama yap ve bir sonraki senkronizasyon döngüsünü bekle
    print("Senkronizasyon hatası: $e");
    break;
  }
}
Enter fullscreen mode Exit fullscreen mode

}
}

Adım 2: Downstream (Sunucudaki Değişikliklerin Yerelle Eşitlenmesi)

Çakışmaları önlemek için sunucu tarafındaki güncel veriler çekilirken en son başarılı senkronizasyon zaman damgası (lastSyncTimestamp) parametre olarak gönderilir.

dart
Future syncDownstream() async {
final lastSync = await _getLastSyncTimestamp();

// Sadece son senkronizasyondan sonra değişen verileri iste
final response = await _apiClient.get('/api/sync?since=${lastSync.toIso8601String()}');

if (response.statusCode == 200) {
final List remoteChanges = response.data['changes'];

await _localDb.writeTxn(() async {
  for (var change in remoteChanges) {
    // Çakışma Kontrolü: Eğer yerelde isDirty true ise yerel veriyi ezme (Conflict Resolution)
    final localEntity = await _localDb.tasks.filter().idEqualTo(change['id']).findFirst();

    if (localEntity == null || !localEntity.isDirty) {
      // Yerelde değişiklik yoksa veya kayıt yeniyse güncelle
      await _localDb.tasks.put(Task.fromJson(change));
    }
  }
});

await _saveLastSyncTimestamp(DateTime.now());
Enter fullscreen mode Exit fullscreen mode

}
}


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

Çevrimdışı dünyada aynı veri hem sunucuda hem de yerelde güncellenmiş olabilir. Bu durumlar için şu stratejilerden biri seçilmelidir:

  1. Last-Write-Wins (LWW): Son güncelleyen kazanır. Zaman damgası (Timestamp) en büyük olan veri kabul edilir.
  2. Server-Wins: Sunucudaki veri her zaman doğru kabul edilir, yerel değişiklikler ezilir.
  3. Client-Wins: Kullanıcının cihazındaki değişiklik önceliklidir.
  4. Merge (Birleştirme): Çakışan alanlar analiz edilerek birleştirilir (Örn: Farklı alanlar güncellendiyse ikisi de korunur).

Sonuç

Offline-First mimari kurmak, ilk etapta karmaşık görünse de doğru veri tabanı seçimi (Isar/SQLite) ve sağlam bir Outbox Tasarımı ile uygulamanızın kalitesini bambaşka bir seviyeye taşır. Ağ hatalarını birer istisna olmaktan çıkarıp uygulamanızın doğal akışının bir parçası haline getirmek, kullanıcı sadakatini artırmanın en kesin yoludur.


Profesyonel Destek ve İletişim

Kurumsal mobil uygulamalarınızda yüksek performanslı veri tabanı mimarileri, senkronizasyon algoritmaları ve Offline-First stratejileri geliştirmek için bir uzmana mı ihtiyacınız var?

Ankara başta olmak üzere tüm dünya çapındaki projelerinize vizyon katmak ve mühendislik kalitesini artırmak için benimle iletişime geçebilirsiniz.

  • Unvan: Nuh Mehmet Demirkol - Lisanslı Bilgisayar Mühendisi
  • Lokasyon: Ankara, Türkiye
  • Portfolyo ve Hizmetler: nmdemirkol.com
  • Doğrudan İletişim (WhatsApp): +90 507 823 68 11

Geleceğin mobil mimarilerini bugünden inşa edelim.

Top comments (0)