Skip to content
projelere açık000%
Tüm yazılar
ENTRY-018Altyapı7 Ağustos 2026 · 3 dk okuma

SER-03 · SER-03.04 · SaaS Sistemleri: Mimari, Maliyet ve Sadelik

Ordovia'da 2,6 milyon request bana SaaS mimarisi hakkında ne öğretti?

Bu vaka Supabase'in pahalı olup olmamasından daha büyük bir ders verdi: sistemin network davranışını açıklayamıyorsam maliyeti de performansı da güvenilir biçimde yönetemem. Önce görünürlük, sonra mimari, en son altyapı değişimi.

Ordovia büyürken Supabase tarafında aylık yaklaşık 2,6 milyon request seviyesine ulaştık. Bu sayı tek başına felaket değildi. Ama ürünün henüz devasa kullanıcı hacmine sahip olmadığı düşünüldüğünde önemli bir soru üretiyordu: bu kadar konuşmayı kim yapıyordu?

Backend faturası bazen fiyat problemi değil, sistemin kendi davranışını açıklayamamasının faturasıdır.

İlk hata: toplam request sayısına bakmak

İlk bakışta 2,6 milyon request tek bir sayı. Fakat optimizasyon için işe yaramıyor. Hangi route? Hangi feature? Kaçı kullanıcı aksiyonuyla, kaçı polling ile, kaçı duplicate mount nedeniyle oluşuyor? Bunları bilmeden yalnızca toplamı düşürmeye çalışırsınız.

ts
type UsefulTelemetry = {  requestsPerActiveUser: number;  requestsPerSession: number;  requestsPerRoute: Record<string, number>;  requestsPerFeature: Record<string, number>;};

İkinci ders: request sayısı ürün mimarisinin yan ürünü

Görevler, takvim, ritüeller, notlar ve başka domain'ler kendi refresh mantığını kurduğunda her biri lokal olarak mantıklı görünebilir. Toplamda ise aynı kullanıcının tek oturumunda birbirinden habersiz network döngüleri oluşur.

txt
feature-local convenience -> system-wide network cost

Bu yüzden performans audit'inde yalnızca yavaş endpoint değil, domain'lerin birbirinden bağımsız verdiği fetch kararlarını da inceliyorum.

Üçüncü ders: cloud-first ile cloud-every-second farklı

Ordovia'nın cihazlar arasında tutarlı olması için cloud kaynak doğrusu önemli. Ama bu her component'in sürekli cloud'a sorması gerektiği anlamına gelmiyor.

ts
const architecture = {  sourceOfTruth: 'cloud',  readStrategy: 'cache-aware',  refreshStrategy: 'event-or-stale-driven',};

Dördüncü ders: polling varsayılan olmamalı

Polling kolaydır çünkü state değişimini düşünmek yerine belirli aralıklarla tekrar sorarsınız. Fakat 30 saniyelik küçük bir loop kullanıcı ve domain sayısıyla çarpıldığında büyük hacim yaratabilir.

txt
1 loop x 2 request/min x 24h x 30d = 86,400 request/month

Üstelik kullanıcı sekmeye bakmıyor olabilir. Bu nedenle visibility, focus, stale time ve gerçek event sinyalleri refresh stratejisinin parçası olmalı.

Beşinci ders: cache performans özelliği değil, sistem politikası

Cache'i yalnızca sayfayı hızlandıran teknik detay olarak görmek eksik. Cache, backend yükünü ve maliyeti kontrol eden ürün altyapısıdır.

ts
const syncPolicy = {  tasks: { staleAfter: 5 * 60_000, refreshOnFocus: true },  calendar: { staleAfter: 10 * 60_000, refreshOnFocus: true },  profile: { staleAfter: 60 * 60_000, refreshOnFocus: false },};

Her domain aynı tazelik ihtiyacına sahip değil. Kullanıcı profili ile yaklaşan takvim etkinliği aynı refresh politikasını kullanmamalı.

Altıncı ders: mutation sonrası her şeyi refetch etmek kolay ama pahalı

Bir görevi tamamladıktan sonra tasks, stats, today, project ve reward verisini topluca yeniden çekmek implementation'ı kolaylaştırır. Fakat sistem büyüdükçe tek kullanıcı aksiyonu request patlamasına dönüşür.

ts
const mutationEffects = {  taskCompleted: ['task', 'today', 'projectStats'],  // Yalnız gerçekten etkilenen domain'leri invalidate et.};

Yedinci ders: altyapı değiştirmek ilk çözüm olmamalı

Yüksek faturayı görünce Supabase'den Firebase'e veya Cloudflare'a geçmek çekici. Fakat aynı polling ve duplicate fetch davranışını yeni servise taşırsanız yalnızca problemin adresi değişir.

txt
measure -> reduce waste -> fix architecture -> then compare infrastructure

Benim için optimizasyon sırası bu hale geldi. Servis değişimi ancak uygulama davranışını düzelttikten sonra anlamlı karşılaştırılabilir.

Sekizinci ders: sistem gözlemlenemiyorsa sistem yönetilemiyor

Bu vaka sonunda en çok önem verdiğim metriklerden biri request / active user / day oldu. Toplam request kullanıcı büyümesiyle doğal olarak artabilir. Kullanıcı başına request artıyorsa mimari davranış değişiyor demektir.

ts
const healthSignal = totalRequests / activeUsers / activeDays;

Ordovia açısından daha büyük ders

Ordovia birçok domain'i tek günlük komuta merkezinde birleştiren bir ürün. Bu ürün avantajı aynı zamanda mimari risk: her yeni domain diğerleriyle ilişki kurdukça görünmeyen coupling artabilir. Bu nedenle ürün seviyesinde 'tek sistem' olmak, kod seviyesinde 'her şey birbirine bağlı' olmak anlamına gelmemeli.

Entegre ürün ile birbirine dolanmış sistem aynı şey değildir.

Bugün başlangıçta neyi farklı yapardım?

  • Her domain için baştan sync/cache policy tanımlardım.
  • Component'lerin doğrudan database erişimini sınırlardım.
  • Request/user/day metriğini erken görünür yapardım.
  • Background polling'i varsayılan olarak kapalı tutardım.
  • Mutation invalidation'ını explicit domain etkileriyle kurardım.
  • Altyapı fiyatını ancak uygulama davranışı normalize olduktan sonra karşılaştırırdım.

2,6 milyon request bana ucuz backend bulmayı değil, kendi sistemimin neden konuştuğunu bilmeyi öğretti. Bu ders yalnız Ordovia için değil, bugün kurduğum bütün SaaS sistemleri için geçerli.

  • Ordovia
  • SaaS Architecture
  • Supabase
  • Performance
  • Cloud
  • Observability

SER-03 · SER-03.04

SaaS Sistemleri: Mimari, Maliyet ve Sadelik

Backend faturası, cloud seçimi ve ürün karmaşıklığını aynı sistem problemi olarak ele alan saha notları ve karar çerçeveleri.

PROJELERE AÇIK · ÜRÜN VE SİSTEM MİMARI · DEVRALMA / STABİLİZASYON / OPERASYON · WEB · ANDROID · WINDOWS · AHMET CANAL

İletişim

Ürününüz sisteminden hızlı mı büyüdü?

Mevcut durumu, en büyük tıkanmayı ve istediğiniz sonucu yazın. Kapsamı birlikte netleştirelim.

Durum

Yeni danışmanlık ve proje işlerine açık.