Kurumsal bir uygulamada belgeler sessizce birikir. Bir satın alma talebine eklenen teklif PDF’i, sözleşmenin imzalı kopyası, sahadan çekilen on fotoğraf; tek başına küçük, toplamda yıl sonunda onlarca gigabayt. Pek çok proje bu dosyaları ilk sürümde veritabanına bytea olarak yazar; yedekleme tek yerde, işlem bütünlüğü hazır, ek altyapı yok. Bedeli bir yıl sonra ödenir: veritabanının yüzde doksanı dosyadır ve her yedek, her taşıma, her sorgu bu ağırlığı taşır.
Tedarik Yönetimi’ni tasarlarken bu yola hiç girmedik: belge deposu ilk günden Cloudflare R2 üzerine kuruldu, veritabanında yalnızca belgeye ait bilgiler tutuldu. Bu yazı, o tasarımda verdiğimiz kararların ve öğrendiklerimizin özeti.
R2 nedir, neden S3 değil?
R2, Cloudflare’ın nesne depolama servisi. Amazon S3 ile aynı API’yi konuşur; yani S3 için yazılmış her istemci kütüphanesi, .NET’te AWSSDK.S3 dahil, yalnızca uç adres değiştirilerek R2’ye bağlanır. Fark iki yerde:
- Çıkış (egress) ücreti yok. S3’te veriyi dışarı çıkarmak, saklamaktan pahalıya gelebilir. R2’de indirme trafiği ücretsiz; bir belgeyi bin kullanıcının indirmesi ile bir kullanıcının indirmesi arasında maliyet farkı yok.
- Fiyat sade. Standart sınıfta saklama GB başına aylık 0,015 dolar; yazma türü işlemler (Class A) milyon başına 4,50 dolar, okuma türü işlemler (Class B) milyon başına 0,36 dolar. Ücretsiz kota ayda 10 GB saklama, 1 milyon yazma ve 10 milyon okuma; küçük bir kurumun belge arşivi çoğu zaman bu kotanın içinde kalır.
Uygulama zaten Cloudflare üzerinde çalışıyorsa (Workers, Pages ya da yalnızca DNS), R2 aynı hesapta ek bir sözleşme, ek bir bulut hesabı gerektirmeden açılır.
Veritabanı ne tutar, R2 ne tutar?
Ayrım baştan nettir. Veritabanı belgeye ait bilgiyi tutar: kime ait, hangi sürüm, kim yükledi, SHA-256 özeti, boyut, içerik türü, nesne anahtarı. Dosyanın baytları ise yalnızca R2’de durur. Böylece veritabanı her zaman küçük ve hızlı kalır; belge arşivi büyüdükçe büyüyen tek şey kovadır.
Her belge sürümü, içeriğin nerede durduğunu söyleyen bir depolama modu alanı taşır. Üretimde bu her zaman R2‘dir; alanın var olma sebebi geliştirme ortamıdır. R2 anahtarları tanımlı değilse uygulama açılışta bunu görür ve içeriği geçici olarak veritabanına yazan moda düşer; geliştirici hiçbir şey kurmadan çalışır. Üretimde ise Required bayrağı açıktır: yarım yapılandırmayla açılmak yerine açılışta durur. Yükleme “başarılı” görünüp içeriğin yanlış yere yazılması, sessiz hataların en pahalısıdır.
Anahtar tasarımı: kovada gezinebilmek
Nesne deposunda “klasör” yoktur, yalnızca anahtar vardır; ama anahtarı iyi tasarlarsanız kova, insan gözüyle gezilebilir bir arşive dönüşür. Kullandığımız biçim:
{sahipTürü}/{sahipAdı}-{sahipId}/{belgeNo}-{belgeId}/v{N}-{sha256:12}.{uzantı}
Project/ornek-santiye-42/EVR-2026-001-7/v1-2d711642b726.pdf
Her parçanın bir gerekçesi var:
- Ad ve kimlik yan yana. Yalnızca kimlik olsaydı kovaya bakan biri hiçbir şey anlamazdı; yalnızca ad olsaydı aynı adlı iki proje aynı klasörde birleşir, ad değişince yol kopardı. İkisi birlikte hem okunabilir hem güvenilir.
- Sürüm numarası ve özet. Aynı belgenin ikinci sürümü yeni bir nesnedir; eskisinin üzerine yazılmaz. Özet, içeriğin bütünlüğünü doğrulamayı ve aynı dosyanın yeniden yüklenmesini fark etmeyi sağlar.
- Kullanıcının dosya adı anahtara girmez. Kullanıcı ne yüklerse yüklesin yol öngörülebilir ve güvenli kalır; özgün ad yalnızca veritabanında saklanır ve indirirken geri verilir.
Bilinçli bir sınır: proje adı değişince eski nesneler taşınmaz. Yol kayıtta duruyor, okuma etkilenmiyor; yalnızca kovadaki görüntü zamanla eskiyor. Taşıma yapmak, bir yeniden adlandırma için binlerce kopyalama işlemi demekti; buna değmez.
.NET istemcisinde iki küçük ayar
S3 istemcisini R2’ye bağlarken iki ayar zaman kazandırır:
new AmazonS3Config
{
ServiceURL = $"https://{accountId}.r2.cloudflarestorage.com",
ForcePathStyle = true, // kova adı ana makine adına değil yola yazılır
AuthenticationRegion = "auto" // R2'nin bölge kavramı yok; imza bunu bekler
};
Yol tabanlı adresleme, kova adında nokta varsa TLS sertifikasıyla çakışmayı önler. auto bölgesi, imzalama hatasının en yaygın nedenidir; unutulursa alınan hata mesajı sizi yanlış yere yönlendirir.
Erişim anahtarları depoya girmez: geliştirmede user-secrets, üretimde ortam değişkeni. Anahtarı yalnızca ilgili kovaya yetkili, “Object Read & Write” izinli bir API token’ı olarak üretmek, sızma durumunda hasarı tek kovayla sınırlar.
Konum ve KVKK
Belgeler kişisel veri içerir: imzalı sözleşmede ad, fotoğrafta yüz, teklifte iletişim bilgisi. Verinin nerede durduğu bu yüzden bir uyum konusudur.
R2’de iki ayrı mekanizma var ve ikisi de kova oluşturulurken belirlenir, sonradan değiştirilemez:
- Konum ipucu performans içindir; “Doğu Avrupa” derseniz veri büyük olasılıkla oraya yerleşir ama garanti yoktur.
- Yargı alanı kısıtı garantidir;
EUseçilen bir kova Avrupa Birliği dışına çıkmaz ve uç adresieu.r2.cloudflarestorage.comolur.
Türkiye’deki bir kurum için KVKK açısından her iki durumda da yurt dışına aktarım söz konusudur ve aydınlatma metninde belirtilmelidir. Yine de AB yargı alanı, yeterlilik ve sözleşme tarafında işleri kolaylaştırır. Kovayı kurarken bu kararı verin; sonradan değiştirmenin tek yolu yeni kova açıp taşımaktır.
İndirme: sunucudan geçirmek mi, imzalı URL mi?
İki yol var. Belgeyi API üzerinden geçirip kullanıcıya vermek, yetki kontrolünü tek yerde tutar ve iz kaydı üretir; bedeli, sunucunun her indirmeye eşlik etmesidir. İmzalı URL ise belirli bir nesne için kısa süreli, yetki içeren bir bağlantı üretir; kullanıcı doğrudan R2’den indirir, sunucu yalnızca bağlantıyı imzalar.
Tedarik Yönetimi’nde şimdilik ilk yolu kullanıyoruz: belge boyutları küçük, “kim, ne zaman indirdi” kaydı denetim için önemli. Büyük dosyalar ya da mobil uygulamadan doğrudan yükleme söz konusu olduğunda imzalı URL doğru araçtır; R2 bunu S3 ile aynı biçimde destekler.
Maliyet örneği
Bin belge sürümü, ortalama 2 MB, yılda beş bin indirme olan bir kurum düşünelim. Saklama 2 GB, ücretsiz kotanın içinde. Yazma bin, okuma beş bin işlem; kotanın çok altında. Yani aylık fatura sıfır. Aynı senaryoda S3, saklama ve çıkış trafiği için birkaç dolar ister; asıl fark, on bin kullanıcı indirdiğinde ortaya çıkar. R2’de fatura değişmez.
Nadiren erişilen arşivler için “Infrequent Access” sınıfı saklamayı GB başına 0,01 dolara indirir, ama otuz günlük asgari süre ve GB başına 0,01 dolar geri alma ücreti gelir. Aktif belge arşivi için standart sınıf kalsın; yasal saklama süresi dolmuş, nadiren açılan belgeler için yaşam döngüsü kuralıyla alt sınıfa taşıma mantıklı.
Kontrol listesi
Belge deposunu R2 üzerine kurarken şunları baştan kararlaştırın:
- Kova adı, yargı alanı ve konum ipucu; sonradan değişmez.
- Anahtar biçimi; okunabilir ve kimliğe dayalı olsun.
- Veritabanında yalnızca belge bilgisi ve nesne anahtarı; baytlar yalnızca kovada.
- Yalnızca o kovaya yetkili API token’ı; anahtarlar ortam değişkeninde.
- Yapılandırma eksikse davranış: geliştirmede veritabanına düş, üretimde dur.
- İndirme yolu: sunucudan geçirme mi, imzalı URL mi; denetim izine göre seçin.
- Aydınlatma metninde yurt dışına aktarım ve saklama süreleri.
Belgeleri ilk günden nesne deposunda tutmak, uygulamanın en az heyecan verici ama en uzun ömürlü tasarım kararlarından biri. Yedekler küçük kalır, taşımalar hızlı olur, dosya ağırlığı hiçbir zaman veritabanı sorgusunun karşısına çıkmaz.