Oyun Kitabı
SDK (Software Development Kit) Sızdırmadan Provider Abstraction: Gateway'in Sınırı (Sdk Sizdirmadan Provider Abstraction)
Provider gateway PSP SDK'sını nasıl sahiplenir, checkout orchestrator neden yalnızca semantik bir arayüz görmelidir? Kart ve wallet akışlarının farklı…
Dağıtık Ödeme Motoru (Distributed Payment Engine)
Bolum 9 / 22
Capture ile complete arasındaki boşluğu kapatan dağıtık ödeme mimarisi serisi.
Önceki bölümde payment evidence ile payment state arasındaki farkı görmüştük: PSP'den gelen kanıt, sistemin kendi kararından farklı bir şeydir. Bu bölümün sorusu daha temel bir sınırla ilgili: checkout orchestrator, bir PSP'nin SDK'sını hiç görmeli mi?
Kısa cevap hayır. Orchestrator'ın bildiği tek şey şu olmalı: bir ödeme talep edildi, bir sonuç döndü. Hangi PSP'nin hangi SDK sürümüyle, hangi HTTP istemcisiyle bu sonucu ürettiği; orchestrator'ın işi değil, provider gateway'in sorumluluğudur.
Checkout Orchestrator
│ ChargeRequest (semantik)
▼
Provider Gateway
│ PSP SDK / HTTP client
▼
PSP A veya PSP B
Bu ayrım basit görünür ama üç yıl sonra hangi PSP'yi değiştirebileceğinizi belirleyen karardır.
Kavramlar ilk geçtiği yerde
📦 Provider Gateway
PSP SDK'sını, kimlik doğrulamasını ve provider'a özgü akışları sahiplenen tek servis.
📦 Semantik Arayüz
Orchestrator'ın gördüğü, hiçbir provider tipine referans vermeyen sözleşme.
📦 Anti-Corruption Layer
Dış sistemin veri modelinin, kendi domain dilinizi kirletmesini engelleyen çeviri katmanı.
📦 Adapter
Provider'a özgü isteği semantik isteğe, provider'a özgü yanıtı semantik sonuca çeviren kod.
Burada 'SDK sızıntısı' teknik bir detay değildir: orchestrator kodunda bir PSP'nin ChargeObject tipi göründüğü an, o PSP'yi değiştirmek artık tek bir dosyayı değiştirmek değil, orchestrator'ın derinliklerine dokunmak anlamına gelir.
Neden SDK sızıntısı sinsi bir borçtur
Bir provider gateway kurarken en kısa yol, PSP'nin SDK nesnelerini olduğu gibi orchestrator'a taşımaktır: ChargeResponse tipini import edip doğrudan kullanmak, bir alanı okumak, statüyü kontrol etmek. İlk haftada hızlıdır. Fakat bu tip orchestrator'ın imzasına girdiği anda iki servis arasında gizli bir versiyon bağı oluşur: SDK güncellenir, alan adı değişir, orchestrator'da derleme hatası ya da daha kötüsü sessiz bir mantık hatası çıkar.
❌ Orchestrator kodu
if (pspResponse.charges.data[0].outcome.network_status === 'approved') { ... }
✓ Orchestrator kodu
if (chargeResult.status === ChargeStatus.Captured) { ... }
İkinci satır hiçbir provider ismi taşımaz. Provider gateway, hangi PSP'nin hangi alanına bakması gerektiğini bilir; orchestrator sadece semantik sonucu bilir.
Kart ve wallet akışları neden aynı arayüzü paylaşıp aynı akışı paylaşmaz
Kart ödemesi çoğunlukla senkrondur: İstek gider, birkaç yüz milisaniye içinde authorize/decline sonucu döner. Wallet veya banka yönlendirmeli ödemeler (kullanıcının PSP sayfasına yönlendirildiği akışlar) asenkrondur: İlk istek yalnızca bir 'pending' durumu ve bir yönlendirme URL'i döndürür; gerçek sonuç dakikalar sonra bir webhook ile gelir.
Kart akışı
ChargeRequest → [senkron çağrı] → ChargeResult (Captured/Declined)
Wallet akışı
ChargeRequest → ChargeResult (Pending + redirectUrl)
...
Webhook → ChargeResult (Captured/Failed) [asenkron, sonradan]
Semantik arayüz her ikisini de aynı ChargeResult şekliyle temsil eder; farkı taşıyan alan statustır (Pending üçüncü bir durum olarak var olur). Orchestrator, 'bu PSP redirect kullanıyor mu' sorusuyla hiç ilgilenmez; yalnızca 'sonuç şu an kesin mi, yoksa beklemede mi' sorusuna cevap alır.
Arayüzü tasarlarken hangi alanlar asla geçmemeli
Provider'a özgü hata kodları, provider'a özgü nesne kimlikleri (örneğin PSP'nin dahili charge id formatı), provider'a özgü meta veri yapıları semantik arayüzden asla dışarı sızmamalı. Bunun yerine gateway bu bilgiyi kendi loglarında, kendi tanılama alanında tutar; orchestrator'a yalnızca kendi correlation id'siyle eşlenmiş bir sonuç döner.
Gateway içinde tutulan (dışarı sızmaz)
provider_raw_code, provider_object_id, provider_response_headers
Orchestrator'a geçen (semantik)
ChargeResult { status, amount, currency, providerRef }
providerRef tek istisnadır: opak bir referans string'idir, destek ve tanılama için saklanır ama üzerinde asla dallanma (branching) yapılmaz.
Sık karıştırılan ayrımlar
❌ SDK'yı bir sınıfa sarmak (wrap) yeterlidir
✓ Sarmalama tip sızıntısını çözmez; davranış hâlâ provider'a özgü kalabilir
❌ Abstraction = interface tanımlamak
✓ Abstraction = orchestrator'ın hiçbir zaman bilmemesi gereken şeyi seçmek
❌ Tek PSP varsa abstraction gereksizdir
✓ Tek PSP'de bile abstraction, test edilebilirlik ve mock'lanabilirlik sağlar
Wrapper ile gerçek abstraction farkı
| Kriter | İnce wrapper | Semantik abstraction |
|---|---|---|
| Tip sızıntısı | Genellikle var | Yok |
| PSP değişince orchestrator etkilenir mi | Evet | Hayır |
| Kart/wallet farkını kim yönetir | Orchestrator | Gateway |
| Test edilebilirlik | PSP mock gerekir | Sahte semantik sonuç yeterli |
Arayüzü tasarlarken kontrol listesi
ChargeResultiçinde herhangi bir PSP'nin isim, alan adı veya hata kodu doğrudan geçiyor mu?- Orchestrator, bir akışın redirect kullanıp kullanmadığını bilmeden
Pendingdurumunu doğru işleyebiliyor mu? - Yeni bir PSP eklerken orchestrator kodunda tek bir satır değişmesi gerekiyor mu? Gerekiyorsa abstraction sızıyor.
- Gateway'in test double'ı, gerçek PSP'ye hiç bağlanmadan tüm semantik durumları üretebiliyor mu?
providerRefdışında hiçbir opak alan orchestrator'ın karar mantığına giriyor mu?
Bu sorulara evet-hayır cevapları, mimari tartışmasını soyut bir 'temiz kod' meselesinden, ölçülebilir bir sınır testine indirger.
Bu yazıdan aklında ne kalmalı
- Provider gateway, SDK'nın ve provider'a özgü her ayrıntının tek sahibidir.
- Orchestrator sadece niyet (ChargeRequest) ve semantik sonuç (ChargeResult) bilir.
- Kart ve wallet akışları aynı arayüzü paylaşır; farkı
statusalanındakiPendingdurumu taşır. providerRefdışında hiçbir opak veya provider'a özgü alan sınırı geçmemelidir.
Bir abstraction'ın gerçek testi, yeni bir provider eklediğinizde orchestrator kodunun hiç değişmemesidir.
Bir sonraki bölümde bu sınırı event tarafına taşıyacağız: gateway'in ürettiği webhook'lar, orchestrator'a ham provider payload'ı olarak mı, yoksa semantik bir event olarak mı ulaşmalı?
SSS
Sık sorulan sorular
Provider Gateway nedir?
PSP SDK'sını, kimlik doğrulamasını ve provider'a özgü akışları sahiplenen tek servis.
Semantik Arayüz nedir?
Orchestrator'ın gördüğü, hiçbir provider tipine referans vermeyen sözleşme.
"SDK'yı bir sınıfa sarmak (wrap) yeterlidir" doğru mu?
Sarmalama tip sızıntısını çözmez; davranış hâlâ provider'a özgü kalabilir
Bu bölüm neyi sabitler?
Bu ayrım basit görünür ama üç yıl sonra hangi PSP'yi değiştirebileceğinizi belirleyen karardır. Provider gateway, SDK'nın ve provider'a özgü her ayrıntının tek sahibidir. Önceki bölümde payment evidence ile payment state arasındaki farkı görmüştük: PSP'den gelen kanıt, sistemin kendi kararından farklı bir şeydir. Bu bölümün sorusu daha temel bir sınırla ilgili: checkout orchestrator, bir PSP'nin SDK'sını hiç görmeli mi?
Ogrenilen Muhendislik Prensipleri
- SDK tipi orchestrator'a sızarsa, PSP değişimi tüm sistemi etkiler.
- Semantik arayüz provider'ı değil niyeti ve sonucu tanımlar.
- Kart ve wallet aynı sözleşmeyi paylaşır, aynı zamanlamayı paylaşmaz.
Okumaya devam et
Okumaya devam et
Seride sonraki yazi
Ham Sağlayıcı Verisi Yerine Anlamsal Olay (Semantic Event)
Provider gateway'in aldığı webhook, downstream'e PSP'nin event adıyla mı, yoksa PaymentCaptured/PaymentFailed gibi semantik bir olayla mı ulaşmalı?
Seride sonraki yazi
Ödeme Kanıtı ve Ödeme Durumu: Neden Karıştırmamalısınız?
Evidence, PSP'nin ne dediğidir. State, sizin ne karar verdiğinizdir. Bu ikisini aynı kayıtta tutarsanız, kurtarma sırasında hangisine güveneceğinizi…
Ayni seriden
Ödeme Hata Taksonomisi
Timeout, 429, 5xx, business decline ve infrastructure hatası aynı şey değildir. Her kategori farklı bir retry politikası ister.