
Backend Servisi
Arayüzü olmayan bir e-ticaret backend'i: 15 rota grubu, 18 Prisma modeli, JWT ve yenileme token'ı ile kimlik, sağlayıcı girişi, oran sınırlama, denetim kaydı ve metrik ucu. Sözleşme Swagger'dan üretiliyor, servis Docker ile paketleniyor.
Arayüzü olan bir uygulamada "çalışıyor mu" sorusunun cevabı ekrandır. Arayüzü olmayan bir serviste böyle bir cevap yok: servis ayakta ama yanlış davranıyor olabilir ve bunu ancak onu tüketen taraf fark eder — çoğu zaman geç. Bu yüzden buradaki asıl problem iş mantığı değil, sözleşme ve görünürlük. İkincisi, bir e-ticaret API'sinde tekrar eden istek sıradan bir durum: ağ kopuyor, istemci yeniden deniyor, ödeme sağlayıcısı aynı bildirimi ikinci kez gönderiyor. "İki kez işlenirse ne olur" sorusunun cevabı her uçta ayrı ayrı düşünülürse er geç bir uç unutuluyor.
İki tüketici tipi. Doğrudan istemci — web veya mobil arayüz: katalog okuyor, sepet ve sipariş yazıyor, oturum yönetiyor. Sunucu tarafı tüketici — ödeme veya kargo sağlayıcısı: webhook gönderiyor, kimliğini paylaşılan bir sır ile kanıtlıyor ve aynı bildirimi tekrarlayabiliyor. Üçüncü bir taraf var: servisi işleten kişi; onun ihtiyacı ekran değil, log, metrik ve denetim kaydı.
Tek geliştirici. Veri modeli ve migration'lar, ara katman zinciri, kimlik ve yetki katmanı, sipariş durum makinesi, denetim ve gözlemlenebilirlik katmanı, Swagger sözleşmesi ve Docker paketi.
Express üzerinde tek servis; istek sabit bir ara katman zincirinden geçiyor: güvenlik başlıkları, kaynak paylaşımı politikası, istek kimliği, oran sınırlama, JWT doğrulama, rol kontrolü, sahiplik kontrolü ve Zod şema doğrulaması. Zincirin sonunda controller yalnız iş mantığını çalıştırıyor; hata yakalama ve hata gövdesinin biçimi tek bir hata ara katmanında toplanıyor, yani her uç aynı hata zarfını üretiyor. Sipariş durumu controller'ın içinde değil, ayrı bir durum makinesi modülünde tanımlı — hangi durumdan hangisine geçilebileceği tek yerde duruyor. Tekrar eden istekler ayrı bir idempotency ara katmanıyla ele alınıyor, yani her uç kendi çözümünü icat etmiyor. Veri tarafında Prisma ve PostgreSQL; ürün araması için şemada ayrı bir arama vektörü kolonu var ve listeleme uçları sayfa numarası yerine imleç tabanlı sayfalama kullanıyor. Gözlemlenebilirlik üç parçalı: yapılandırılmış log, istek kaydı ve metrik ucu.
Tekrar eden istek bir uca özgü değil; sepete ekleme, sipariş oluşturma ve webhook işleme aynı sorunu paylaşıyor. Çözüm ara katmana alındığında bir uç bunu unutamıyor — koruma rotanın tanımına iliştiriliyor, controller'ın disiplinine değil.
Ödünleşimİstemcinin idempotency anahtarını üretmesi ve tekrar denemede aynı anahtarı göndermesi gerekiyor; bunu yapmayan bir istemci korumadan yararlanamıyor. Ayrıca işlenmiş anahtarların saklandığı yer sürekli büyüyor ve temizlenmesi gerekiyor.
Sipariş yaşam döngüsü sistemin en çok dallanan yeri: ödeme, hazırlık, kargo, teslim, iptal ve iade. Geçiş kuralları controller'lara dağılırsa aynı kural birkaç yerde farklı yazılıyor ve zamanla ayrışıyor. Kuralları tek modülde toplamak, geçerli geçişlerin tek bir yerde okunmasını sağlıyor.
ÖdünleşimKural uygulamada duruyor, veritabanında değil: doğrudan SQL ile yapılan bir güncelleme durum makinesini atlayabiliyor. Ustura'da randevu çakışmasını veritabanı kısıtına indirdiğim gibi burada da geçişleri veri seviyesinde zorlamak mümkündü; yapılmadı.
Erişim token'ı kısa ömürlü ve durumsuz; yenileme token'ı ise veritabanında bir kayıt olarak duruyor. Bunun sebebi iptal edilebilirlik: durumsuz bir yenileme token'ı süresi dolana kadar geçerli kalıyor ve çalınmış bir oturumu kapatmanın yolu olmuyor. Sağlayıcı girişleri de ayrı bir hesap modeliyle ilişkilendiriliyor, yani aynı kullanıcı birden fazla giriş yöntemi taşıyabiliyor.
ÖdünleşimHer yenileme isteği bir veritabanı okuması demek — durumsuz bir tasarımın ölçekleme avantajı burada bilerek bırakılıyor. Tablo da zamanla büyüyor ve süresi geçmiş kayıtların temizlenmesi gerekiyor.
Metrik ucu sistemin iç durumunu, Swagger arayüzü ise tüm sözleşmeyi anlatıyor. İkisi de bir saldırgan için keşif materyali. Her ikisi de kimlik doğrulama arkasına alındı; webhook ucu ayrıca paylaşılan bir sır ile doğrulanıyor.
ÖdünleşimDokümantasyonun kapalı olması, API'yi tüketecek bir geliştirici için ek bir erişim adımı demek. Metrik ucunun korunması da toplayıcı tarafında ayrı yapılandırma gerektiriyor — kimlik doğrulamayı desteklemeyen bir toplayıcıyla doğrudan çalışmıyor.
Bir isteğin gövdesini log'a yazmak hata ayıklamayı kolaylaştırıyor, ama aynı gövdede parola, token ve adres olabiliyor. Maskeleme, log yazma anında ve alan bazlı yapılıyor; hassas alan log'a hiç girmiyor, sonradan temizlenmiyor.
ÖdünleşimMaskelenecek alanların listesi elle tutuluyor; yeni bir hassas alan eklendiğinde listeye yazılması gerekiyor ve bunu hatırlatan bir mekanizma yok. Maskelenen bir alan hata ayıklamada da görünmüyor, yani bazı hatalar log'dan izlenemiyor.
Express, ara katman zincirinin okunabilir olması için seçildi: isteğin hangi sırayla neyden geçtiği kod okunarak takip edilebiliyor ve bu, güvenlik kararlarının görünür kalması demek. Prisma, 18 modelli bir şemada ilişkileri ve migration geçmişini takip edilebilir tutuyor; şema dosyası aynı zamanda veri modelinin dokümantasyonu. Zod, istek gövdesinin doğrulanmasını tek bir ara katmanda toplarken tipleri de aynı şemadan veriyor. Swagger sözleşmesi kodun yanında, yorum olarak yazılıyor — ayrı bir dokümanın koddan ayrışması bu şekilde zorlaşıyor. Docker, servisin bağımlılıklarıyla birlikte tek bir çalıştırılabilir birim olmasını sağlıyor; arayüzü olmayan bir serviste "benim makinemde çalışıyor" cümlesinin karşılığı yok.
Ara katman zinciri güvenlik başlıklarıyla başlıyor ve kaynak paylaşımı politikası açıkça tanımlı. Oran sınırlama uç bazında yapılandırılmış; kimlik uçlarında daha dar. Parolalar bcrypt ile saklanıyor, erişim token'ı kısa ömürlü, yenileme token'ı veritabanında tutulduğu için iptal edilebiliyor. Yetki üç kademeli: kimlik doğrulama, rol kontrolü ve kaynak sahipliği kontrolü — yani doğru rolde olan bir kullanıcı bile başkasının kaydını düzenleyemiyor. Webhook ucu paylaşılan sır ile doğrulanıyor. Dosya yüklemeleri ayrı bir ara katmandan geçiyor. Metrik ve Swagger uçları kimlik doğrulama arkasında. Değişiklikler ayrı bir denetim kaydı modeline yazılıyor; log alanları yazma anında maskeleniyor.
Bu bir backend servisi; sayfa hızı ölçümü anlamlı değil ve saklanmış bir yük testi çıktısı da bulunmadığı için buraya bir sayı yazılmadı. Yapısal tarafta üç karar var: listeleme uçları imleç tabanlı sayfalama kullanıyor, böylece derin sayfalarda sorgu maliyeti sabit kalıyor; ürün araması için şemada ayrı bir arama vektörü kolonu tutuluyor, yani arama her istekte metin taraması yapmıyor; metrik ucu istek sayısı ve süresi gibi değerleri dışarı verdiği için servisin davranışı tahminle değil ölçümle izlenebiliyor.
Bu repoda otomatik test yok ve bir backend servisinde bu, en pahalı eksik. Test edilecek en kolay ve en değerli yer belli: sipariş durum makinesi zaten saf bir modül, girdi-çıktı olarak test edilebilir ve sistemin en çok dallanan kuralını taşıyor. Bugün oradan başlar, ardından idempotency ara katmanını iki kez gönderilen aynı istekle doğrulardım. İkinci nokta durum kuralının yeri: geçişleri uygulamada tuttum, oysa Ustura'da çakışma kuralını veritabanı kısıtına indirdiğimde koruma her yazma yoluna aynı anda uygulanmıştı. Burada doğrudan SQL ile yapılan bir güncelleme durum makinesini atlayabilir; bugün en azından geçersiz duruma düşmeyi engelleyen bir kontrol kısıtı eklerdim.
Ne yapmak istediğinizi anlatın; ne kadar süreceğini ve nereden başlanacağını baştan söylerim.