Flutter & Mobil İle İleri Düzey Veri Önbellekleme Yapısı Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Bu rehberi uygulamak için Flutter SDK 3.24 veya üzeri bir sürümün yüklü olması gerekmektedir. Projenizde kullanacağımız temel kütüphaneler şunlardır: Hive (NoSQL anahtar-değer deposu), Drift (SQLite tabanlı relasyonel veritabanı) ve Dio (HTTP istemcisi). Projenizi oluşturduktan sonra pubspec.yaml dosyanıza şu bağımlılıkları ekleyin:

dependencies:
  hive: ^4.0.0
  hive_flutter: ^4.0.0
  drift: ^2.20.0
  dio: ^5.7.0
  path_provider: ^2.1.0

Bu kütüphaneler, yerel veritabanı yönetimi ve ağ istekleri için endüstri standardı araçlardır. Geliştirme ortamınızda build_runner paketini de dev-dependencies altında tanımladığınızdan emin olun, çünkü kod üretimi (code generation) süreci bu rehberin temelini oluşturacaktır.

Veri Önbellekleme Stratejileri: Karşılaştırmalı Analiz

Hangi veriyi nerede saklayacağınız, uygulamanın mimari başarısını belirler. Aşağıdaki tablo, farklı veri türleri için en uygun önbellekleme yöntemlerini özetlemektedir.

Yöntem Kullanım Amacı Avantajı Dezavantajı
Hive Basit, sık değişen veriler Çok hızlı, hafif İlişkisel sorgu desteği yok
Drift (SQLite) Karmaşık, ilişkisel veriler Güçlü sorgulama, ACID uyumu Daha fazla konfigürasyon
SharedPreferences Küçük ayarlar, tercihler Basit kullanım Büyük veri için uygun değil

Adım Adım Hive ile Hızlı Veri Önbellekleme

Hive, anahtar-değer (key-value) yapısı sayesinde anlık veri önbellekleme için idealdir. Öncelikle bir veri modeli tanımlayıp bunu Hive için uyumlu hale getirmeliyiz. İşte bir kullanıcı profilini önbelleğe alma örneği:

import 'package:hive/hive.dart';

part 'user_model.g.dart';

@HiveType(typeId: 0)
class UserProfile extends HiveObject {
  @HiveField(0)
  final String id;
  @HiveField(1)
  final String username;

  UserProfile({required this.id, required this.username});
}

Bu kodda @HiveType anotasyonu, Hive'ın veriyi ikili (binary) formatta serileştirmesini sağlar. HiveObject sınıfından türetmek, veritabanı üzerindeki silme ve güncelleme işlemlerini nesne üzerinden doğrudan yapmanıza olanak tanır.

Drift ile İlişkisel Veritabanı Yönetimi

Uygulamanızda karmaşık veri tabloları varsa, Drift kullanmak en güvenli yoldur. Drift, SQLite üzerinde tip güvenli bir katman sunar. Aşağıdaki örnekte, bir "Ürünler" tablosu oluşturuyoruz:

import 'package:drift/drift.dart';

class Products extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get name => text().withLength(min: 1, max: 50)();
  RealColumn get price => real()();
}

Drift kullanımı, veriler arasındaki tutarlılığı (referential integrity) korumak için SQL dilinin gücünü Flutter'a taşır. Veritabanı şemanızı değiştirdiğinizde, dart run build_runner build komutu ile tüm kod tabanını güncel tutmanız gerekmektedir.

Ağ İstekleri ve Önbellek Senkronizasyonu

İleri düzey bir yapıda, ağ isteği atmadan önce yerel veritabanını kontrol etmeliyiz. Buna "Cache-First" (Önce Önbellek) stratejisi denir. Dio interceptor yapısı ile bunu otomatize edebiliriz.

class CacheInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    final cachedData = box.get(options.path);
    if (cachedData != null) {
      return handler.resolve(Response(requestOptions: options, data: cachedData));
    }
    super.onRequest(options, handler);
  }
}

Bu interceptor, her ağ isteğinden önce yerel kutuyu (box) kontrol eder. Eğer veri varsa, ağ isteğini yapmadan yerel veriyi döndürür. Bu, kullanıcıya anlık tepki veren bir arayüz sunar.

Güvenlik Uyarısı: Yerel veritabanında sakladığınız hassas verileri (token, kişisel bilgiler) mutlaka şifreleyin. Hive için Hive.generateSecureKey() kullanarak veritabanı seviyesinde AES-256 şifreleme uygulayın. Şifrelenmemiş veritabanları, cihazın root edilmesi durumunda kolayca okunabilir.

Veri Tutarlılığı ve "Stale-While-Revalidate"

Önbellekteki verinin eskimesini önlemek için "Stale-While-Revalidate" (Eski veriyi göster, arka planda güncelle) yaklaşımını kullanmalıyız. Bu, kullanıcıya hızlı veri sunarken, arka planda en güncel veriyi çekip arayüzü güncellememizi sağlar.

Future fetchData() async {
  // 1. Yerel veriyi göster
  final localData = box.get('data');
  updateUI(localData);

  // 2. Arka planda güncelle
  final remoteData = await api.getData();
  box.put('data', remoteData);
  updateUI(remoteData);
}

Bu yaklaşım, uygulamanın "donmuş" gibi görünmesini engeller ve her zaman güncel kalmasını sağlar. Hatalı ağ isteklerini yönetmek için mutlaka try-catch blokları kullanın.

Sıkça Sorulan Sorular

Yerel veritabanı boyutu nasıl yönetilir?

Veritabanı boyutunu yönetmek için eski verileri silen bir "TTL" (Time-to-Live) mekanizması kurmalısınız. Belirli bir süre geçtikten sonra box.delete() metodunu çağırarak önbelleği temizleyin.

Hive mı yoksa Drift mi kullanmalıyım?

Basit anahtar-değer çiftleri ve hızlı erişim için Hive; karmaşık, ilişkisel ve büyük ölçekli veriler için Drift tercih edilmelidir.

Önbellek verisi bozulursa ne yapmalıyım?

Veritabanı dosyalarını başlatırken bir try-catch bloğu ile veritabanının okunabilirliğini kontrol edin. Hata durumunda dosyayı silip yeniden oluşturmak (factory reset) en güvenli yoldur.

Ağ bağlantısı yokken veri nasıl güncellenir?

Connectivity Plus paketi ile bağlantı durumunu dinleyin. Bağlantı tekrar geldiğinde, yerel veritabanındaki "bekleyen işlemler" kuyruğunu sunucuya gönderin.

Şifreleme performansı etkiler mi?

Modern cihazlarda AES şifreleme donanımsal olarak desteklendiği için performans kaybı ihmal edilebilir düzeydedir. Güvenlik için bu maliyete mutlaka katlanmalısınız.

Sorumluluk Reddi: Bu makaledeki kod örnekleri eğitim amaçlıdır. Üretim ortamında veri güvenliği, SQL injection koruması ve veri gizliliği yasalarına (KVKK/GDPR) uyum sağlamak geliştiricinin sorumluluğundadır.

Repository Tasarım Deseni ile Veri Soyutlama

Veri önbellekleme mimarinizi ölçeklenebilir kılmak için "Repository Pattern" kullanmak en iyi pratiktir. Bu desen, uygulamanızın UI katmanının verinin nereden geldiğini (API mi, Hive mı, yoksa Drift mi) bilmemesini sağlar. Böylece veri kaynağını değiştirdiğinizde iş mantığınızı güncellemek zorunda kalmazsınız.

Aşağıdaki örnek, bir UserRepository sınıfının yerel ve uzak veri kaynaklarını nasıl koordine ettiğini göstermektedir:

abstract class UserRepository {
  Future getUser(String id);
}

class UserRepositoryImpl implements UserRepository {
  final UserApi _api;
  final UserLocalDataSource _local;

  UserRepositoryImpl(this._api, this._local);

  @override
  Future getUser(String id) async {
    try {
      final remoteUser = await _api.fetchUser(id);
      await _local.saveUser(remoteUser);
      return remoteUser;
    } catch (e) {
      final cachedUser = await _local.getUser(id);
      if (cachedUser != null) return cachedUser;
      throw Exception("Veri kaynağına ulaşılamadı.");
    }
  }
}

Önbellek Katmanında Birim Testleri (Unit Testing)

Veri önbellekleme yapınızın güvenilirliğini sağlamak için, özellikle veri senkronizasyon mantığınızı test etmeniz kritiktir. mockito veya mocktail paketlerini kullanarak ağ isteklerini simüle edebilir ve yerel veritabanı davranışlarını doğrulayabilirsiniz.

Aşağıdaki test senaryosu, ağ hatası durumunda uygulamanın önbellekten veri çekip çekmediğini doğrular:

void main() {
  test('Ağ hatası durumunda önbellekten veri dönmelidir', () async {
    final mockApi = MockUserApi();
    final mockLocal = MockUserLocalDataSource();
    
    when(mockApi.fetchUser(any)).thenThrow(Exception('Ağ hatası'));
    when(mockLocal.getUser('1')).thenAnswer((_) async => User(id: '1', name: 'Test'));

    final repo = UserRepositoryImpl(mockApi, mockLocal);
    final user = await repo.getUser('1');

    expect(user.name, 'Test');
    verify(mockLocal.getUser('1')).called(1);
  });
}

Performans İzleme ve Hata Ayıklama İpuçları

Büyük ölçekli uygulamalarda önbellek yapısının performansını izlemek için şu stratejileri uygulayabilirsiniz:

  • Veritabanı İndeksleme: Drift veya SQL tabanlı çözümlerde, sık sorgulanan alanlara (örneğin user_id veya timestamp) mutlaka indeks ekleyin.
  • DevTools Kullanımı: Flutter DevTools içerisindeki "Network" sekmesini kullanarak gereksiz ağ çağrılarını, "Database" sekmesini kullanarak ise yerel veritabanı boyutunu ve sorgu sürelerini takip edin.
  • Batch İşlemler: Çok sayıda veriyi aynı anda yerel veritabanına yazarken, tek tek insert işlemleri yerine transaction veya batch yapılarını kullanın. Bu, I/O yükünü ciddi oranda azaltır.
// Drift ile Batch İşlem Örneği
await database.batch((batch) {
  batch.insertAll(usersTable, userList);
});

Bu optimizasyonlar, özellikle düşük donanımlı cihazlarda uygulamanızın "donma" (jank) yaşamadan veri işlemesini sağlar. Veritabanı işlemlerini her zaman ana izlekten (main isolate) ayırarak compute fonksiyonu ile arka planda çalıştırmayı da değerlendirebilirsiniz.

Sonuç

Flutter ile ileri düzey veri önbellekleme yapısı kurmak, uygulamanızın profesyonel ve hızlı hissettirmesinin anahtarıdır. Cache-first yaklaşımı ve Stale-While-Revalidate stratejisi ile kullanıcılarınıza kesintisiz bir deneyim sunabilirsiniz. Bir sonraki adım olarak, veritabanı işlemlerinizi Repository tasarım deseni ile soyutlayarak, veri kaynağını (yerel veya ağ) kodunuzun geri kalanından bağımsız hale getirmeyi deneyebilirsiniz.

Bu yazıya tepkinizi paylaşın:
Selin Yılmaz

Kullanıcı odaklı rehberler hazırlama konusunda uzmanım. Adım adım anlatımlarla karmaşık süreçleri herkes için anlaşılır kılıyorum.

Yorumlar (0)

Yorum Yaz