Fin ve Tech: Onboarding'de Doğru Mimari ve Doğru Risk Skorlama

Fin ve Tech serisinin bu bölümünde, onboarding ve KYC yazımızın ikinci parçasındayız. İlk bölümde süreci ve temel kavramları ele almıştık; bu kez işin mühendislik tarafına, yani doğru mimari kurulumuna ve doğru risk skorlamaya odaklanıyoruz.
Bir onboarding başvurusu düşünün — kimlik doğrulama, sanctions taraması, kredi bürosu sorgusu, ML risk modeli ve compliance onayı. Tüm bu adımlar farklı sistemlerde, farklı hızlarda ve farklı güven seviyelerinde gerçekleşiyor.
Bu yazıda, gerçek bir onboarding karar mekanizmasını hibrit risk skorlama ve state machine ile nasıl inşa edeceğimizi ve bunu yaparken 4 kritik mimari prensibi nasıl uygulayacağımızı inceleyeceğiz.
Neden “Tek Tip Onboarding” Çalışmaz?
Çoğu ekibin ilk yaptığı hata, tüm müşterilere aynı derinlikte kontrol uygulamaktır.
Klasik alternatifler neden yetersiz kalır?
- Herkese aynı derinlikte kontrol: Düşük riskli müşteriyi kaybettirir, yüksek riskliyi gözden kaçırır.
- Tek katmanlı skorlama (sadece kural ya da sadece model): Ya esneklikten ya açıklanabilirlikten ödün verir.
Doğru mimari bu sorunları ortadan kaldırır.
Başvurular birbirine bağlı değil, risk seviyesine göre gevşek bağlı (loosely coupled) yollara ayrılır.
Bu yazıda öğrenecekleriniz:
- Hibrit Risk Scoring ile kural + ML skorlama katmanlarının birleştirilmesi
- Onboarding State Machine ile denetlenebilir karar akışı
- Channel-Agnostic Architecture ile şube/dijital kanal tutarlılığı
- Ongoing Monitoring ile tek seferlik değil sürekli KYC
Mimari Genel Bakış
Sistemimiz, risk seviyesine göre dallanan tek bir karar mekanizması etrafında kuruludur.
Her kanal (şube, mobil, web) aynı merkezi servise bağlanır:
Branch / Mobile / Web
│
▼
┌──────────────────────┐ ┌────────────────────────────┐
│ Onboarding Gateway │──────▶ │ Risk Decision Engine │
│ (Channel-Agnostic) │ │ │
└──────────────────────┘ │ RuleEngine (Sanctions/PEP) │──▶ HIGH → Reject
│ MLModel (Probabilistic) │──▶ LOW → Approve
└────────────────────────────┘ │
│ MEDIUM → ManualReview
▼
OnboardingStateMachine
│
▼
Ongoing Monitoring (post-onboarding)
Decision Flow: Started → RiskScoringPending → Approved / ManualReviewRequired / Rejected
Use Case’ler: Mimarinin Gerçek Hayattaki Karşılığı
Mimari kararlarını somutlaştırmadan önce, bu kararların hangi senaryolardan doğduğuna bakalım.
Düşük riskli bireysel müşteri: Telefon + OTP, kimlik + selfie ile liveness/face match, MERNİS sorgusu, otomatik low-risk skoru → 15-30 saniyede insan müdahalesi olmadan onboard.
Yüksek riskli kurumsal müşteri: UBO (Ultimate Beneficial Owner) çıkarımı, her ortağın bireysel KYC’si, sanctions/PEP taraması, gelir kaynağı belgeleri, compliance onayı → 1-3 iş gününde EDD ile tamamlanır. Burada öncelik hız değil, doğruluk ve izlenebilirliktir.
Kripto borsası: Tek seferlik KYC yerine sürekli transaction monitoring; ani hacim artışları yeniden risk skorlamasını tetikler; müşteri profili periyodik olarak (örn. 6 ayda bir) yeniden değerlendirilir.
Geleneksel banka (şube + dijital): Şube personeli kimlik taramasını yapar, mobil kullanıcı kendi selfie’sini çeker — ikisi de aynı merkezi risk skorlama servisini çağırır. Kanal farkı, kural setinde farklılığa dönüşmemelidir.
Pattern 1: Hibrit Risk Scoring
Problem: Tek Katmanın Yetersizliği
// ❌ YANLIŞ: Modele tüm kararı bırakmak
var score = await _riskModel.PredictAsync(input);
return score < 0.5 ? RiskLevel.Low : RiskLevel.High;
// Sanctions listesindeki biri model hatasıyla "low risk" çıkabilir
// ve regülatöre bu kararı açıklayamazsınız
Sadece sabit kurallar regülatöre açıklanabilir ama esnek değildir.
Sadece bir ML modeli esnek ama denetlenemezdir.
Çözüm: Kural Katmanı Önce, Model Katmanı Sonra
Kesin ihlaller kural katmanında elenir; kalan başvurular için model bir olasılık skoru üretir:
public enum RiskLevel { Low, Medium, High }
public class RiskScoringService
{
private readonly ISanctionsListService _sanctionsList;
private readonly IRiskModelClient _riskModel;
public RiskScoringService(ISanctionsListService sanctionsList, IRiskModelClient riskModel)
{
_sanctionsList = sanctionsList;
_riskModel = riskModel;
}
public async Task<RiskLevel> EvaluateAsync(CustomerApplication application)
{
// 1. Kural tabanlı katman: kesin ihlaller burada elenir
if (await _sanctionsList.IsListedAsync(application.FullName, application.NationalId))
{
return RiskLevel.High;
}
if (application.IsPoliticallyExposed)
{
return RiskLevel.High;
}
// 2. ML tabanlı katman: kalan başvurular için olasılıksal skor
var modelScore = await _riskModel.PredictAsync(new RiskModelInput
{
Age = application.Age,
Country = application.Country,
DeviceFingerprint = application.DeviceFingerprint,
TransactionIntent = application.ExpectedMonthlyVolume
});
// 3. Skor -> seviye dönüşümü (eşikler compliance tarafından belirlenir)
return modelScore switch
{
< 0.3 => RiskLevel.Low,
< 0.7 => RiskLevel.Medium,
_ => RiskLevel.High
};
}
}
Peki kural katmanı neden her zaman önce çalışır?
Bir model hiçbir zaman sanctions listesindeki bir ismi “düşük risk” olarak skorlayabilecek pozisyonda bırakılmamalıdır. Kurallar açıklanabilirliği, model ise esnekliği sağlar.
İkisi birlikte çalıştığında hem regülatöre hesap verilebilir hem de görünmeyen riskler yakalanır.
Pattern 2: Onboarding State Machine
Problem: İzlenemeyen Kararlar
// ❌ YANLIŞ: Durumu ayrı bir flag olarak tutmak
application.IsApproved = true;
application.ReviewNote = "ok";
// Hangi adımda hangi karar neden verildi? Geriye dönük izlenemiyor.
Onboarding state’lerini veritabanında dağınık flag’ler olarak tutmak, bir başvurunun neden reddedildiğini veya hangi adımda takıldığını geriye dönük izlemeyi çok zorlaştırır.
Bu da regülatör denetimlerinde kritik bir gerekliliktir.
Çözüm: Açık State Machine
public enum OnboardingState
{
Started,
IdentityVerificationPending,
RiskScoringPending,
ManualReviewRequired,
Approved,
Rejected
}
public OnboardingState GetNextState(OnboardingState current, RiskLevel risk, bool identityVerified)
{
return current switch
{
OnboardingState.Started when identityVerified
=> OnboardingState.RiskScoringPending,
OnboardingState.Started when !identityVerified
=> OnboardingState.Rejected,
OnboardingState.RiskScoringPending when risk == RiskLevel.Low
=> OnboardingState.Approved,
OnboardingState.RiskScoringPending when risk == RiskLevel.Medium
=> OnboardingState.ManualReviewRequired,
OnboardingState.RiskScoringPending when risk == RiskLevel.High
=> OnboardingState.Rejected,
_ => current
};
}
Bu yaklaşımın avantajı, her geçişin denetlenebilir ve test edilebilir olmasıdır.
Bir başvurunun neden reddedildiğini, hangi state’te hangi kararın verildiğini net şekilde gösterebilirsiniz.
Pattern 3: Channel-Agnostic Architecture
Problem: Kanal Başına Ayrı Kural Seti
// ❌ YANLIŞ: Her kanal kendi kural setini uyguluyor
if (channel == "branch")
return _branchRiskRules.Evaluate(application);
else if (channel == "mobile")
return _mobileRiskRules.Evaluate(application);
// İki kanal aynı müşteriye farklı risk seviyesi verebilir
Bir banka hem şubeden hem mobil uygulamadan gelen başvuruları aynı KYC motoruna bağlamak istediğinde, kanal başına ayrı kural seti kaçınılmaz olarak tutarsızlık üretir.
Çözüm: Tek Merkezi Servis, Çoklu Giriş Noktası
// OnboardingGateway.cs — kanal sadece veri toplama şeklini değiştirir
public async Task<RiskLevel> SubmitApplicationAsync(CustomerApplication application, ChannelType channel)
{
application.SourceChannel = channel; // sadece metrik/log amaçlı
// Kanal ne olursa olsun aynı karar mekanizması çalışır
return await _riskScoringService.EvaluateAsync(application);
}
Şubedeki personel kimlik taramasını yapar, mobil kullanıcı kendi selfie’sini çeker.
Ama ikisi de aynı RiskScoringService’i çağırır.
Hangi kanaldan gelirse gelsin, sonuç tutarlı ve denetlenebilir olur.
Pattern 4: Ongoing Monitoring
Problem: “KYC Bir Kere Yapılır, Biter” Yanılgısı
// ❌ YANLIŞ: Onboarding sonrası risk skorunu hiç güncellememek
var risk = await _riskScoringService.EvaluateAsync(application);
account.RiskLevel = risk; // sonsuza kadar bu değerde kalır
Bir kripto platformu gibi yüksek hacimli ürünlerde, tek seferlik KYC yeterli değildir.
Müşteri ilişkisi boyunca sürekli izleme yapılması gerekir.
Çözüm: Periyodik Yeniden Değerlendirme + Tetikleyiciler
// OngoingMonitoringService.cs
public async Task ReEvaluateAsync(Guid customerId, TransactionEvent recentActivity)
{
var volumeSpike = recentActivity.Amount > _thresholds.VolumeSpikeLimit;
if (volumeSpike || _scheduler.IsDue(customerId, TimeSpan.FromDays(180)))
{
var updatedRisk = await _riskScoringService.EvaluateAsync(
await _customerRepository.GetApplicationSnapshotAsync(customerId));
await _customerRepository.UpdateRiskLevelAsync(customerId, updatedRisk);
}
}
Ani işlem hacmi artışları yeniden risk skorlamasını tetikler; müşteri risk profili ayrıca periyodik olarak (örn. 6 ayda bir) yeniden değerlendirilir.
Bu, KYC’nin bir kerelik kapı değil, sürekli açık bir kontrol noktası olduğunu gösteren en net örnektir.
Pattern Özeti
| Pattern | Problem | Çözüm | Bileşen |
|---|---|---|---|
| Hibrit Risk Scoring | Tek katmanın yetersizliği | Kural önce, model sonra | RiskScoringService.cs |
| Onboarding State Machine | İzlenemeyen kararlar | Açık state geçişleri | OnboardingStateMachine.cs |
| Channel-Agnostic Architecture | Kanal başına tutarsızlık | Tek merkezi karar servisi | OnboardingGateway.cs |
| Ongoing Monitoring | Tek seferlik KYC yanılgısı | Periyodik + tetiklenen yeniden skorlama | OngoingMonitoringService.cs |
Sık Yapılan Yanlışlar
❌ “Risk skorlamayı senkron, tek bir çağrıda yapalım”
✅ Sanctions, PEP, kredi bürosu gibi farklı kaynaklara yapılan çağrılar paralel ve asenkron tasarlanmalı; tek bir yavaş servis tüm akışı kilitlememeli
❌ “Model skoru tek başına karar versin, kurallara gerek yok”
✅ Kural katmanı olmadan model kararları regülatöre açıklanamaz hale gelir
❌ “Onboarding state’lerini veritabanında ayrı bir flag olarak tutalım, yeter”
✅ Açık bir state machine olmadan, hangi adımda neden takıldığını veya reddedildiğini geriye dönük izlemek çok zorlaşır
❌ “Risk eşiklerini kod içine sabit yazalım”
✅ Eşikler, compliance ekibinin kod deploy etmeden güncelleyebileceği bir konfigürasyon katmanında tutulmalı
Sonuç: Mimari, Risk Skorlamanın Taşıyıcısıdır
Doğru risk skorlama, doğru mimari olmadan hayata geçemez.
Bu yazıda gördüğümüz gibi:
- Her müşteri segmenti farklı bir onboarding yolu gerektirir
- Kural ve model katmanları birlikte çalıştığında hem açıklanabilirlik hem esneklik sağlanır
- State machine gibi açık yapılar, hem mühendisliği hem denetimi kolaylaştırır
- Kanal bağımsızlığı ve ongoing monitoring, tutarlılığı kalıcı kılar
Onboarding mimarisi, bir ürün kararı kadar bir risk yönetimi kararıdır. İkisini ayrı düşünen ekipler, er ya da geç ikisinden birini feda eder.
Her zaman olduğu gibi kaynaklara mutlaka göz atılmasını tavsiye ediyorum. ⬇️
- FATF — Guidance on Digital Identity (https://www.fatf-gafi.org/content/dam/fatf-gafi/guidance/Guidance-on-Digital-Identity.pdf.coredownload.pdf)
- MASAK — Mali Suçları Araştırma Kurulu Mevzuatı (https://masak.hmb.gov.tr/mevzuat-tamami)
- World Bank — Digital ID to Enhance Financial Inclusion: A Toolkit for Regulatory Authorities (https://documents1.worldbank.org/curated/en/099650005162214653/pdf/P16477001277440f10b8080dc6f51daf2dc.pdf)
- OWASP — Cheat Sheet Series (https://cheatsheetseries.owasp.org/index.html)
- Martin Fowler — State Machine (https://martinfowler.com/dslCatalog/stateMachine.html)

