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.

Distributed payment engine architecture diagram

Ö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

  1. ChargeResult içinde herhangi bir PSP'nin isim, alan adı veya hata kodu doğrudan geçiyor mu?
  2. Orchestrator, bir akışın redirect kullanıp kullanmadığını bilmeden Pending durumunu doğru işleyebiliyor mu?
  3. Yeni bir PSP eklerken orchestrator kodunda tek bir satır değişmesi gerekiyor mu? Gerekiyorsa abstraction sızıyor.
  4. Gateway'in test double'ı, gerçek PSP'ye hiç bağlanmadan tüm semantik durumları üretebiliyor mu?
  5. providerRef dışı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ı

  1. Provider gateway, SDK'nın ve provider'a özgü her ayrıntının tek sahibidir.
  2. Orchestrator sadece niyet (ChargeRequest) ve semantik sonuç (ChargeResult) bilir.
  3. Kart ve wallet akışları aynı arayüzü paylaşır; farkı status alanındaki Pending durumu taşır.
  4. providerRef dışı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

Seride sonraki yazi

Ayni seriden

DENEME

Timeout, 429, 5xx, business decline ve infrastructure hatası aynı şey değildir. Her kategori farklı bir retry politikası ister.

Paylaş