MicroFrame platformundaki servisler birbirini çağırmaz. Aralarında gRPC yok, REST yok, ağ üstünden “hızlıca bir bakayım” yok. Bir servis, başka bir servisin sahibi olduğu bir bilgiye ihtiyaç duyarsa o bilginin kendi kopyasını tutar ve bu kopya olaylarla beslenir. Bilerek bıraktığımız tek bir istisna var: API geçidi lisans özetini imzalı bir akışla lisans servisinden alır. Bunu 12 Eylül 2026’da kural olarak yazdık. O gün, senkron çağrıların bize neye mal olduğunu açıkça gördük.
Kuralı doğuran gün
O günün büyük kısmını servisler arası güven altyapısına harcamıştık: makine çağıranlar için kimlik bilgisi, yetki devri, A servisinin B servisine kimin adına çalıştığını kanıtlamasını sağlayan tesisat. Bunların hepsi üç çağrıyı desteklemek içindi. Dağıtım servisi bir işleme izin olup olmadığını politika servisine soruyordu. İki içerik servisi bilgi bankası makalelerini CMS’ten istiyordu.
Bu üç çağrıya yakından baktığımızda hiçbirinin senkron olması gerekmediğini gördük. Birinin durumu daha da kötüydü: dağıtım servisi, politika servisinin hiç sunmadığı bir metodu çağırıyordu. Her çağrı Unimplemented ile dönüyor, her dağıtım reddediliyordu. Hata da “yetkiniz yok” diye okunmaya çok müsaitti; oysa asıl söylediği “böyle bir metot yok” idi.
Saydık da. Çekirdek servislerin çoğu zaten başka bir servise tek bir çağrı bile yapmıyordu. Kurduğumuz altyapı bir avuç çağrı noktasına hizmet edecekti ve o noktaların her birinin olayla çözülebilecek bir karşılığı vardı.
Çağrının yerine ne geçiyor
Bir servisin ötekine sorduğu üç tür soru var. Üçünün de olayla verilen bir cevabı var.
“X’in şu anki durumu ne?” Sahibi durum değişikliklerini yayınlar. Tüketen servis yerel bir projeksiyon tutar ve onu herhangi bir tablo gibi okur. Okuma servisin dışına çıkmadığı için sahibin yavaşlaması ya da düşmesi okuyanı yavaşlatmaz, düşürmez.
“Lütfen X’i yap.” İsteyen XRequested yayınlar. Sahibi işi yapar ve aynı korelasyon kimliğiyle XCreated yayınlar. İsteyen sonucu, beklemede kaldığı bir cevaptan değil, bu olaydan öğrenir.
“Bu çağıran X’i yapabilir mi?” Her servis kendi politika denetim noktasını, kendisini ilgilendiren izinlerin yerel kopyası üzerinde çalıştırır. Yetki kararı ağ üstünden politika servisine sorularak değil, isteğin geldiği yerde verilir.
Olaylar servisten nasıl çıkıyor
Her servis olaylarını, onları doğuran değişiklikle aynı veritabanı işleminde (transaction) bir outbox tablosuna yazar. Bir aktarıcı bu satırları Kafka’ya taşır. İşlem geri alınırsa olay hiç var olmamış olur. Kafka erişilemezse olay tabloda bekler.
Bir ders bize olması gerekenden pahalıya patladı: outbox’a kaydedilmemiş bir olay türü sessizce düşüyordu. Satır tabloda kalıyor, aktarıcı onu hiç almıyor, tüketen servis değişikliği hiç duymuyordu. Hiçbir yerde hata yoktu. Artık her olay türü servis açılırken baştan kaydediliyor. Bu, olay başına hatırlanması gereken bir ayrıntı değil, servisin kurulma biçiminin parçası.
Yeniden kurulabilen projeksiyonlar
Tüketenin bütünüyle ihtiyaç duyduğu durumlar için (kim hangi kiracıya ait, bir rol hangi izinleri veriyor) sahibi, varlık anahtarıyla sıkıştırılmış (compacted) bir Kafka konusuna yazar. Kafka her anahtar için son kaydı saklar. Silme işlemi de bir “tombstone” kaydıdır.
Bu, tüketene kullanışlı bir özellik kazandırır: konuyu baştan okuyarak bütün projeksiyonunu yeniden kurabilir. API geçidimiz tam olarak bunu yapıyor. Her geçit pod’u konuları kendi tüketici grubuyla okur, açılışta baştan oynatır ve projeksiyonu dolana kadar 503 döner. Yeni açılan bir pod, yarım yüklenmiş veriden cevap vermez.
Bedeli
Projeksiyon nihai tutarlılıkla (eventual consistency) çalışır. Az önce verilen bir izin henüz her serviste görünmüyor olabilir ve ekranların bunu dürüstçe göstermesi gerekir. Bu bedeli kabul ediyoruz, çünkü alternatifi daha kötüydü: bir servisin yavaşlamasının bütün çağıranları yavaşlattığı ve eksik bir metodun reddedilmiş bir istek gibi göründüğü senkron çağrı zincirleri.
Kural, işi de öne çeker. Başka bir servisin verisine ihtiyaç duyan yeni bir özellik, ekran yazılmadan önce olay ve projeksiyonla başlar. İlk gün daha yavaştır. Sonraki her gün daha hızlı oldu.
Kuralı nasıl uyguluyoruz
Bir platform servisi başka bir platform servisine istemci eklemeden önce, aynı bilgiyi taşıyacak olayı yazıyoruz. Eylülden bu yana incelediğimiz her çağrı noktasının böyle bir olayı vardı.
ByteGurus
Yazar