Tüm yazılar Arka Uç

NoSQL’de Veri Modelleme: Tabloyu Unutup Ekranı Düşünmek

İlişkisel veritabanında veriyi normalleştirirsiniz; belge tabanlısında ekranı modellersiniz. Bu farkı geç anlamanın maliyetini ödedikten sonra kurduğum modelleme yaklaşımını anlatıyorum.

DT
Demir Taşdemir Mobil Uygulama & Web Geliştirici
— dk okuma

Belge tabanlı bir veritabanına ilişkisel alışkanlıklarla girdiğinizde her şey yolunda gidiyor gibi görünüyor — ta ki bir ekranı doldurmak için beş ayrı yerden veri çekmeniz gerekene kadar. Ben bu duvara ilk projede çarptım ve modeli baştan kurmak zorunda kaldım.

Sorun şuydu: veriyi ilişkisel dünyada öğrendiğim gibi modellemiştim. Kullanıcılar bir yerde, ilanlar başka yerde, kategoriler ayrı, yorumlar ayrı. Her şey tertemiz, hiçbir veri tekrar etmiyor. Sonra bir ilan listesi ekranı yaptım ve her satır için ilan sahibinin adını ve fotoğrafını göstermem gerekti. Yirmi ilan, yirmi ek okuma.

Temel kural: sorgu ile başlayın, veriyle değil

İlişkisel tasarımda önce varlıkları ve ilişkileri çıkarırsınız; sorgular sonra gelir, çünkü JOIN her şeyi birleştirebilir. Belge tabanlısında böyle bir güvenceniz yok. Dolayısıyla süreç tersine dönüyor:

  1. Uygulamanın hangi ekranları var?
  2. Her ekran hangi veriyi, hangi sırayla, hangi filtreyle gösteriyor?
  3. O ekranı tek sorguyla dolduracak belge yapısı nedir?

Yani veri modeli, varlıkların değil ekranların şekline göre çıkıyor. Bu ilk başta yanlış geliyor — çünkü aynı bilgi birden fazla yerde duruyor. Ama belge tabanlı sistemlerde depolama ucuz, okuma pahalı. Optimizasyon hedefi değişiyor.

Denormalizasyon: kopyalamak hata değil, karar

Yukarıdaki ilan listesi örneğinde yaptığım şey, ilan belgesinin içine sahibinin adını ve fotoğraf adresini kopyalamak oldu:

ilanlar/{ilanId}
{
  "baslik": "Kadıköy'den Ataşehir'e",
  "tarih": "2026-07-02T08:30:00Z",
  "fiyat": 120,
  "durum": "aktif",
  "sahip": {
    "id": "u_918",
    "ad": "Demir T.",
    "fotograf": "https://.../u918.jpg"
  }
}

Artık liste ekranı tek sorguyla doluyor. Ama yeni bir sorun doğuyor: kullanıcı adını değiştirdiğinde, onun bütün ilanlarındaki kopya eskimiş oluyor.

Bu soruna verilecek cevap “o zaman kopyalamayalım” değil. Cevap şu soru: bu verinin eski kalması ne kadar zarar verir?

VeriDeğişme sıklığıEski kalmasıKararım
Kullanıcı adı, avatarNadirKatlanılabilirKopyala, arka planda güncelle
İlan fiyatıOrtaKabul edilemezTek kaynakta tut
Kategori adıÇok nadirKatlanılabilirKopyala
Bakiye, stokSıkKabul edilemezAsla kopyalama

Kopyaladığım alanları güncellemek için, kaynak belge değiştiğinde tetiklenen bir arka plan işi kullanıyorum. Kullanıcı adını değiştirdiğinde, o kullanıcının ilanları toplu güncelleniyor. Anlık değil, ama birkaç saniye içinde tutarlı hale geliyor — ve o birkaç saniye hiçbir kullanıcıyı rahatsız etmiyor.

Kopyalanan alanı, kaynağından ayırt edilebilir tutun

Belge içinde sahip.ad gibi iç içe bir nesne kullanmamın sebebi sadece düzen değil: bu alanların kopya olduğu, isimlendirmeden anlaşılıyor. Düz bir sahipAdi alanı zamanla “gerçek kaynak burası mı” sorusunu doğuruyor. Kopyaları bir arada tutmak, sonradan kodu okuyan kişiye — ki o kişi genelde altı ay sonraki sizsiniz — hangi verinin türetilmiş olduğunu söylüyor.

Alt koleksiyon mu, dizi mi, ayrı koleksiyon mu

Bu, en sık verilen ve en sık yanlış verilen karar. Kullandığım ölçüt üç soruya dayanıyor:

  • Kaç tane olabilir? Sayı sınırsız büyüyebiliyorsa dizi olmaz. Belge boyutunun bir üst sınırı var ve bir gün ona çarparsınız.
  • Ana belgeyi okurken hep gerekiyor mu? Gerekiyorsa ve sayı küçükse dizi mantıklı — ek okuma yapmıyorsunuz.
  • Kendi başına sorgulanıyor mu? “Bütün kullanıcıların son yorumları” gibi bir sorgu varsa, veri alt koleksiyonda değil kök koleksiyonda durmalı.

Pratikte oturttuğum dağılım şöyle: bir ilanın etiketleri diziye giriyor (az sayıda, hep beraber okunuyor). Bir ilanın teklifleri alt koleksiyona giriyor (çok olabilir, ayrı sorgulanıyor). Bildirimler ise kök seviyede ayrı bir koleksiyonda duruyor, çünkü kullanıcıya göre filtreleniyor ve ilanla doğrudan bağlı değil.

Sayaçlar: en sinsi problem

“Bu ilana kaç teklif geldi” sorusunu cevaplamak için alt koleksiyonu saymak, ilk bakışta doğal görünüyor. Ama her listeleme ekranında her ilan için sayma yapmak, hem yavaş hem pahalı.

Çözüm, sayıyı belgenin içinde tutmak:

ilanlar/{ilanId}
{
  "baslik": "...",
  "teklifSayisi": 14,
  "sonTeklifTarihi": "2026-07-02T09:12:00Z"
}

Bu sayıyı elle güncellemek hata kaynağı. Ben teklif ekleme ve silme işlemlerini tek bir işlem (transaction) içinde sayaçla birlikte yapıyorum: ya ikisi de olur ya hiçbiri. Aksi halde uygulama arada kapandığında sayı gerçekle uyuşmuyor ve bunu fark etmek aylar sürebiliyor.

Tek belgeye çok hızlı yazmayın

Çok sayıda kullanıcının aynı anda güncellediği tek bir sayaç belgesi darboğaz oluyor — belge başına yazma hızının bir sınırı var. Çok yüksek trafikli sayaçlarda sayıyı birden fazla parçaya bölüp okurken toplamak gerekiyor. Bunu her sayaç için yapmaya gerek yok; sadece gerçekten sıcak olanlar için.

Sorguyu düşünmeden alan eklemeyin

Belge tabanlı sistemlerde sorgu yetenekleri sınırlı: birden fazla alanda aralık filtresi, karmaşık OR kombinasyonları, metin içinde arama gibi şeyler ya mümkün değil ya da özel bir indeks istiyor.

Bu yüzden filtrelenecek alanları filtreye uygun saklıyorum:

  • Sıralama yapılacak tarihleri karşılaştırılabilir tek bir biçimde tutmak
  • Çok kullanılan filtre kombinasyonlarını tek bir bileşik alanda birleştirmek — örneğin "aktif_kadikoy" gibi
  • Metin aramasını veritabanına yaptırmaya çalışmamak; arama için ayrı bir yol kurmak

İkinci maddedeki bileşik alan, ilk bakışta çirkin duruyor. Ama iki ayrı alanda filtreleme yapmak için gereken indeksten çok daha ucuz ve öngörülebilir çalışıyor.

Modeli değiştirmek zorunda kalınca

Şema olmaması, şema değişikliği olmayacağı anlamına gelmiyor — sadece değişikliği veritabanı zorlamıyor, siz yönetiyorsunuz. Yeni bir alan eklerken izlediğim yol:

  1. Yeni alanı yazmaya başla, eskisini de yazmaya devam et.
  2. Okuma tarafında yeni alan varsa onu, yoksa eskisini kullan.
  3. Mevcut belgeleri arka planda toplu olarak dönüştür.
  4. Sahadaki eski uygulama sürümleri yeterince azalınca eski alanı yazmayı bırak.
  5. En son eski alanı sil.

Bu beş adım fazla gibi görünüyor ama mobil uygulamada zorunlu: kullanıcının telefonundaki eski sürüm, siz veriyi değiştirdiğinizde de çalışmaya devam etmek zorunda.

Özet

Belge tabanlı veritabanına geçerken bırakılması gereken tek alışkanlık var: veriyi tekrar etmeme refleksi. Yerine koyulacak alışkanlık ise, her model kararında “bu hangi ekranı besliyor ve o ekran kaç okumaya mal oluyor” sorusunu sormak. Doğru model, en temiz görünen değil, ekranı en az işle dolduran model.

  • Arka Uç
  • Veritabanı
  • NoSQL
  • Mimari
  • Firebase
Paylaş: LinkedIn X WhatsApp
DT

Demir Taşdemir

Mobil Uygulama & Web Geliştirici

2018'den beri yazılım geliştiriyorum. App Store ve Google Play'de 11 uygulama yayınladım; şu an 6 mobil uygulama, 1 e-ticaret platformu ve 1 masaüstü oyun üzerinde çalışıyorum.

Veri modeliniz büyüdükçe zorlaşıyor mu?

Belge tabanlı bir veritabanında model kararları, sonradan değiştirmesi en pahalı kararlar. Mevcut yapınızı gözden geçirip nerede sıkıştığınızı çıkarabiliriz. İletişim sayfasından yazabilirsiniz.