İçeriğe geç

PoC’den Production’a: Ölçeklenebilir Yazılım Mimarisi, Sistem Güvenilirliği ve Maliyet Mühendisliği

Bir sipariş ve ödeme sistemini PoC’den production’a taşıyoruz: SQL transaction, idempotency, ödeme telafisi, SLO, yük testi, AI güvenliği ve ölçülebilir mühendislik maliyeti.

· TankDev Mühendislik

Bir sipariş demosu birkaç saatte ikna edici görünebilir: sepete ürün eklenir, ödeme servisine istek atılır ve ekranda “Sipariş alındı” yazısı belirir. Üretim ortamında asıl soru şudur: müşteri aynı düğmeye iki kez bastığında, banka parayı çekip yanıtı kaybettiğinde veya deploy sırasında worker kapandığında sistem hangi gerçeği koruyacaktır?

Bu yazıda tek bir B2B sipariş ve ödeme sistemini PoC’den canlı operasyona taşıyoruz. PostgreSQL sipariş, stok rezervasyonu ve ödeme durumunun yetkili kaynağıdır. Sipariş kabulü senkron; tahsilat, bildirim ve opsiyonel AI destek işleri asenkrondur. Her mimari kararın doğruluk, gecikme, toparlanma ve maliyet üzerinde bir karşılığı vardır.

Senaryonun başlangıç sözleşmesi
BoyutVarsayım / hedef
İş hacmiAyda 1 milyon uygun API isteği; bunun 100 bini yeni sipariş kabulü.
TrafikNormal 50 request/s; kampanya piki 200 request/s; kısa stres basamağı 400 request/s.
Veri100 kiracı, 100 bin SKU, 1 milyon geçmiş sipariş; stok tenant + SKU düzeyinde.
DoğrulukStok negatif olamaz; aynı iş anahtarı ikinci sipariş veya ikinci tahsilat oluşturamaz.
Kullanıcı deneyimiSipariş 202 + order_id ile kabul edilir; ödeme sonucu ayrı durum sorgusu / bildirimle gelir.

Aylık ortalama yaklaşık 0,39 request/s iken 200 request/s pik varsayımı bilinçli olarak çok daha yüksektir: B2B sistemleri kampanya ve toplu aktarım anlarında yoğunlaşabilir. Altyapı boyutlandırması pik ve gecikme bütçesine, birim maliyet ise gerçekleşen aylık hacme göre yapılır. İkisini karıştırmak ya fazla kapasiteye ya da kuyruk çökmesine yol açar.

1. Proof of Concept, MVP ve Production-Ready Sistemler Arasındaki Mimari Farklar

PoC “Bu entegrasyon mümkün mü?” sorusunu cevaplar. Ödeme sandbox’ı, tek uygulama süreci ve örnek veri yeterli olabilir. MVP “Bu iş akışı gerçek kullanıcı için değer yaratıyor mu?” sorusunu sınar; daha az özellik içerir, fakat gerçek para işliyorsa kimlik doğrulama, veri bütünlüğü ve temel mutabakat ertelenemez. Production-ready ise tanımlı yükte, belirli hata koşullarında ve işletilebilir bir maliyetle verilen sözün tutulmasıdır.

Aynı ürünün üç farklı mühendislik sözleşmesi
AlanPoCMVPProduction-ready
VeriGeçici / örnekKalıcı veri + migrationConstraint, audit, saklama ve geri yükleme provası
ÖdemeMutlu yol sandboxGerçek sağlayıcı + idempotencyBelirsiz sonuç, telafi, webhook ve mutabakat
OperasyonGeliştirici elle çalıştırırİzleme + yedekSLO, nöbet, runbook, rollback, kapasite kanıtı
Güvenlikİzole test erişimiTenant ve rol kontrolleriYetki testleri, secret rotation, kötüye kullanım sınırları

İlk üretim adımı, Orders, Inventory, Payments ve Notifications modüllerini açık arayüzlerle ayıran modüler monolittir. API ve worker aynı kod tabanından farklı süreçler olarak çalışabilir. Aralarındaki dayanıklı sınır PostgreSQL outbox’ıdır. Bellek içi kuyruk, process restart olduğunda sipariş işini kaybettireceğinden ödeme için uygun değildir.

Sipariş API’si, PostgreSQL transaction ve outbox, ödeme worker’ı, sağlayıcı ve opsiyonel AI arasındaki sorumluluk sınırları.

İki API instance’ı aynı VPS üzerindeyse uygulama process hatasına karşı fayda sağlar; host veya disk kaybına karşı yüksek erişilebilirlik sağlamaz. Örneğimizde production topolojisi ayrı hata alanlarına yayılan iki API düğümü, bağımsız worker kapasitesi ve test edilmiş failover/PITR özelliği olan PostgreSQL hizmeti hedefler. Küçük bir işletme tek VPS ile başlayabilir; bunun RTO, kesinti ve veri kaybı sınırlarını açıkça kabul etmelidir.

2. Distributed Systems: Concurrency, Race Conditions ve Veri Tutarlılığı

Son ürün stokta bir adetken iki istek de önce SELECT ile “1” okur, sonra UPDATE ile “0” yazarsa iki sipariş kabul edilebilir. Birden çok API süreci varken uygulama içi mutex yeterli değildir. Doğruluk, veriyi değiştiren transaction ve veritabanı constraint’leriyle korunmalıdır. Para için float yerine en küçük para biriminde bigint, para birimi için ayrı alan kullanırız; tutar ve indirimleri istemciden güvenerek almayız.

Atomik stok rezervasyonu ve outbox

Aşağıdaki parça uygulamanın parametre bağlayan SQL sürücüsü için hazırlanmıştır; iki noktalı isimler bind parametreleridir. API önce aynı transaction içinde idempotency kaydının sahipliğini alır. Stok UPDATE’i bir satır döndürmezse uygulama ROLLBACK yapar ve sonraki INSERT’leri çalıştırmaz. Başarılı durumda sipariş, rezervasyon, outbox ve tekrar döndürülecek yanıt aynı COMMIT ile kalıcılaşır.

SQL / PostgreSQL — bound parameters
BEGIN;
-- Prerequisites:
-- inventory PRIMARY KEY (tenant_id, sku), CHECK (available >= 0)
-- idempotency PRIMARY KEY (tenant_id, operation, request_key)
-- orders UNIQUE (tenant_id, request_key)
-- reservations UNIQUE (tenant_id, order_id, sku)
-- outbox PRIMARY KEY (event_id)
-- Tenant-scoped foreign keys connect reservations to orders/inventory.
-- The idempotency claim and payload-hash check happen here (see flow below).

UPDATE inventory
SET available = available - :qty
WHERE tenant_id = :tenant_id AND sku = :sku
  AND :qty > 0 AND available >= :qty
RETURNING available;
-- Application MUST verify exactly one row; otherwise ROLLBACK.

INSERT INTO orders
  (id, tenant_id, request_key, amount_minor, currency, status)
VALUES
  (:order_id, :tenant_id, :request_key, :server_total, :currency,
   'PAYMENT_PENDING');

INSERT INTO reservations
  (tenant_id, order_id, sku, qty, state)
VALUES (:tenant_id, :order_id, :sku, :qty, 'HELD');

INSERT INTO outbox (event_id, tenant_id, event_type, aggregate_id, payload)
VALUES (:event_id, :tenant_id, 'PaymentRequested', :order_id, :payload_json);

UPDATE idempotency
SET http_status = 202, response_body = :accepted_response_json
WHERE tenant_id = :tenant_id AND operation = 'POST /orders'
  AND request_key = :request_key;
COMMIT;
-- Call the payment provider only AFTER commit, from a worker.

Read Committed altında bu koşullu UPDATE, aynı stok satırına gelen yazmaları sıraya alır ve bekleyen işlem güncel satırda koşulu tekrar değerlendirir. Bu, bütün iş kurallarını otomatik olarak serializable yapmaz. Çok SKU’lu sepette satırlar sabit SKU sırasıyla kilitlenir ve herhangi bir eksiklikte tamamı geri alınır. Satırlar arası karmaşık invariant için açık kilit veya Serializable gerekebilir; serialization failure ve deadlock durumunda transaction bütünü sınırlı olarak yeniden denenir. Ayrıntı: PostgreSQL transaction isolation.

Idempotency: aynı isteğin aynı iş sonucuna bağlanması

Anahtarı yalnızca Redis’e yazmak yeterli değildir: TTL, eviction veya failover sonrasında aynı ödeme tekrar oluşabilir. Kalıcı tekillik PostgreSQL’dedir. Kapsam tenant + işlem + request_key; payload_hash ise normalize edilmiş ürün, adet, para birimi ve fiyat teklifinin hash’idir. İstemci aynı mantıksal denemede anahtarı korur; farklı sipariş için yeni anahtar üretir.

Pseudocode / API transaction
authenticate_and_authorize_tenant()
validate_payload_and_server_side_quote()
with db.transaction():
    claimed = INSERT_idempotency_ON_CONFLICT_DO_NOTHING_RETURNING_key(
        tenant, "POST /orders", key, canonical_payload_hash
    )
    if not claimed:
        saved = SELECT_idempotency_FOR_UPDATE(tenant, operation, key)
        if saved.payload_hash != canonical_payload_hash:
            return HTTP_409_KEY_REUSED_WITH_DIFFERENT_PAYLOAD
        return saved.http_status, saved.response_body
    # Execute stock/order/reservation/outbox SQL above.
    # Persist the 202 response before COMMIT.
return HTTP_202(order_id, status_url)

# A concurrent INSERT waits for the conflicting transaction.
# If its owner rolls back, another caller can claim the key.
# If it commits, replay reads the committed response in a new statement.
# On lock timeout: roll back, return retryable error; keep the SAME key.

Sipariş anahtarı ile sağlayıcı anahtarı farklı ömürlere sahiptir. Worker, payment_attempt kaydını ağ çağrısından önce kalıcılaştırır; sağlayıcıya tenant/order/attempt kimliğinden türeyen sabit bir anahtar gönderir. Timeout sonrası yeni attempt açılmaz. Saklama süresi sağlayıcının idempotency penceresiyle birlikte tasarlanır; pencere aşılmışsa kör tekrar yerine durum sorgusu ve mutabakat gerekir. Stripe’ın idempotency sözleşmesi bunun sağlayıcıya ve API sürümüne göre değişebileceğini gösterir.

3. Fault Tolerance ve Resilience Engineering: Sistemler Hata Verdiğinde Ne Olur?

Dağıtık ödeme işleminde PostgreSQL COMMIT’i ile banka tahsilatını tek yerel transaction’a alamayız. Dolayısıyla “HTTP timeout oldu, ödeme başarısız” çıkarımı yanlıştır. Üç farklı sonuç gerekir: doğrulanmış başarı, doğrulanmış ret ve henüz bilinmeyen sonuç. Siparişi her hatada CANCELLED yapmak, parası alınmış müşterinin stok hakkını kaybetmesine neden olabilir.

Ödeme durum makinesi ve izin verilen hareketler
Durum / olayKararKalıcı yan etki
PAYMENT_PENDINGWorker attempt’i açarRezervasyon HELD kalır.
PAYMENT_UNKNOWNSağlayıcı durumunu sorgula / webhook bekleYeni tahsilat anahtarı üretme.
Kesin ret → PAYMENT_FAILEDTransaction ile rezervasyonu serbest bırakSiparişi CANCELLED yap; PaymentFailed outbox’ı yaz.
Kesin başarı → PAIDTutar / para birimi / merchant eşleşmesini doğrulaOrderPaid outbox’ı yaz; sevkiyatı tekilleştir.
PAID + teslim edilemiyor → REFUND_PENDINGAyrı refund anahtarıyla telafi başlatREFUNDED yalnız sağlayıcı teyidinden sonra.
Yanıtı kaybolan tahsilatın PAYMENT_UNKNOWN, sağlayıcı sorgusu ve gerektiğinde iade ile çözülmesi.

Somut hata: O-104 siparişinin 1.250 TL tahsilatı sağlayıcıda tamamlandı, fakat worker yanıtı alamadan kapandı. Lease süresi dolunca başka worker aynı attempt’i devralır; aynı sağlayıcı anahtarıyla güvenli tekrar veya sağlayıcı işlem referansıyla sorgu yapar. İmzalı webhook önce geldiyse order zaten PAID olabilir. Durum geçişi order satırı kilitlenerek ve mevcut durum kontrol edilerek yapılır; ikinci OrderPaid olayı ve ikinci sevkiyat üretilmez.

Belirsiz ödeme için rezervasyon sonsuza kadar sessizce tutulmaz: örneğin 5 dakikada otomatik mutabakat, 15 dakikada operasyon kuyruğu ve müşteriye görünür durum bilgisi hedeflenir. Bu süreler örnektir; iş politikasıyla belirlenir. UNKNOWN durumunda TTL doldu diye stok serbest bırakılıp sipariş kesin başarısız sayılmaz. Sağlayıcının nihai durumu bulunamıyorsa kontrollü manuel inceleme gerekir; bekleyen tutar ve rezervasyon yaşı ayrı alarm üretir.

Kesin ret senaryosunda telafi stok rezervasyonunun çözülmesidir. İşlem, rezervasyonu HELD → RELEASED olarak koşullu değiştirir; yalnız değişen satır varsa stok geri eklenir ve sipariş aynı transaction’da iptal edilir. Böylece webhook ve zamanlanmış görev yarışsa bile stok iki kez artmaz. Tahsilat başarılı fakat sipariş karşılanamıyorsa telafi iadedir: refund isteği ayrı outbox olayıdır; iade de timeout verebilir ve REFUND_PENDING izlenerek mutabakatla tamamlanır. Saga, veritabanı rollback’i gibi geçmişi silmez; yeni bir iş işlemiyle etkisini telafi eder.

Outbox worker’ı kısa transaction’da FOR UPDATE SKIP LOCKED ile iş seçer, süreli lease ve sahiplik token’ı kaydeder, ardından transaction’ı kapatıp dış çağrıyı yapar. ACK yalnız aynı token ile yazılır; süresi dolan işler tekrar alınabilir. Bu model en az bir kez işlemeye yol açar; uçtan uca exactly-once iddiası yerine benzersiz event_id, tüketici inbox’ı, idempotent iş geçişleri ve sağlayıcı anahtarı kullanırız. Poison message sınırlı denemeden sonra inceleme kuyruğuna alınır. PostgreSQL SKIP LOCKED kuyruk tüketimi için yararlıdır; genel amaçlı tutarlı rapor okuması değildir.

Timeout, retry ve circuit breaker aynı problemi çözmez

Örnek bütçe: API sipariş kabulü için 800 ms üst sınır, DB lock beklemesi için 100 ms; ödeme worker’ında connect timeout 300 ms ve çağrının tamamı için 3 saniye. Bunlar ölçümle ayarlanacak başlangıç değerleridir. Retry yalnız transient hata sınıfında, sağlayıcı sözleşmesi ve kalan deadline izin veriyorsa uygulanır; örneğin ilk çağrı + en fazla iki tekrar, full jitter ile artan bekleme. İş kuralı reddi tekrar denenmez. Sağlayıcıya ulaşıp ulaşmadığı belirsiz çağrı reconciliation yoluna gider.

Circuit breaker kötüleşen sağlayıcıya yük bindirmeyi durdurur; bulkhead ödeme worker’ının bağlantı ve eşzamanlılık havuzunu diğer işlerden ayırır. Kuyruk yaşına ve uzunluğuna sınır koymak backpressure sağlar. Üç katmanda üçer deneme 27 aşağı akış çağrısına dönüşebilir; retry tek sorumlu katmanda yönetilir. Tasarım dayanağı: AWS — timeout, retry ve jitter.

4. Observability ve SRE: Çalışan Bir Sistem Gerçekten Sağlıklı mı?

CPU yüzde 20 ve /health yanıtı 200 iken müşteriler ödeme sonucu alamıyor olabilir. Liveness process’in canlılığını, readiness trafik kabul yeteneğini, iş SLI’ı ise kullanıcıya verilen sözün tutulmasını ölçer. Üçüncü taraf kesintisini liveness’a bağlayıp bütün process’leri restart etmek problemi büyütür. Sipariş kabulü ve ödeme tamamlanması ayrı SLI’lar olmalıdır.

Örnek hizmet hedefleri — ölçülmüş sonuç değil
GöstergeTanım ve hedef
Kabul erişilebilirliği30 günde uygun sipariş isteklerinin ≥ %99,9’u doğru 202 veya belgelenmiş iş sonucu almalı.
GecikmeAynı uygun kabul isteklerinin ≥ %95’i 300 ms içinde tamamlanmalı; p95 ≤ 300 ms.
Ödeme sonuçlanmasıKabul edilmiş ödeme işlerinin ≥ %99’u 60 saniyede doğrulanmış sonuca ulaşmalı; sağlayıcı kesintileri dahildir.
Doğruluk alarmıÇift tahsilat / negatif stok / tenant veri sızıntısı: hedef sıfır, tek olay inceleme başlatır.
KuyrukOldest unprocessed age normalde < 10 s; > 60 s ve büyüme varsa alarm.

Uygun istek kümesi sözleşmede sabittir: geçerli kimlik ve şemaya sahip checkout istekleri. Beklenen stok yok / kart reddi iş sonucu olarak ayrı sayılır; sunucu 5xx, deadline aşımı ve kapasite yetersizliğinden doğan 429 başarısızlıktır. Saldırı trafiğine uygulanan tanımlı limitler ayrı raporlanır. Payda sonradan değiştirilerek SLO iyileştirilemez. Aylık 100 bin uygun sipariş isteğinde %99,9 erişilebilirlik bütçesi 100 kötü istektir; %95 gecikme hedefi ise en fazla 5 bin isteğin 300 ms’yi aşmasına izin verir. Bunlar farklı bütçelerdir.

Metrikler rate, error ve duration histogramlarını; loglar durum geçişi ve hata sınıfını; distributed trace ise API → DB → outbox → worker → sağlayıcı yolunu gösterir. Asenkron sınırda trace context mesajla taşınır veya span link ile ilişkilendirilir. order_id ve trace_id log/trace alanıdır; her siparişi metric label yapmak yüksek cardinality ve maliyet üretir. Kart verisi, token, prompt içindeki kişisel bilgiler loglanmaz. OpenTelemetry sinyalleri ortak bir dil sağlar.

PromQL / example — histogram buckets in seconds
# 5-minute p95; compute over merged buckets, never average per-instance p95.
histogram_quantile(0.95,
  sum by (le) (
    rate(order_acceptance_duration_seconds_bucket{eligible="true"}[5m])
  )
)

# Failure ratio: counter includes 5xx, deadline and capacity rejection.
sum(rate(order_acceptance_bad_total[5m]))
/
clamp_min(sum(rate(order_acceptance_eligible_total[5m])), 0.000001)

# 99.9% availability => error budget fraction = 0.001
# burn_rate = observed_failure_ratio / 0.001

Dashboard tek başına nöbet politikası değildir. Örneğin 1 saat ve 5 dakika pencerelerinde birlikte 14,4× bütçe tüketimi görülürse çağrı açılır; düşük hacimde en az olay eşiği ve sentetik akış kontrolü eklenir. Alarmın yanında sorumlu, runbook, sağlayıcı durumu kontrolü, güvenli trafik kısma ve incident kaydı bulunur. Error budget hızla tükeniyorsa riskli deploy’lar durdurulur; müşteri etkisi ve tekrarını önleyen iş takip edilir. Google SRE — SLO uygulama rehberi bu yaklaşımın temelini anlatır.

5. Performance Engineering: Capacity Planning ve Ölçeklenebilirlik

“Daha fazla sunucu ekleriz” bir kapasite planı değildir. Sipariş API’si I/O beklerken worker sağlayıcı kotasına, PostgreSQL ise sıcak stok satırının kilidine takılabilir. Little’s Law ile ortalama in-flight iş yaklaşık L = λ × W’dir. 200 request/s ve 120 ms varsayılan ortalama yanıt süresi yaklaşık 24 eşzamanlı istek demektir. Burada p95 değil ortalama kullanılır. Bu sayı DB bağlantı sayısına eşit değildir; bağlantının ne kadar tutulduğu ayrıca ölçülür.

Kapasite testi gerçek müşteri verisiyle veya canlı ödeme sağlayıcısına yük bindirerek yapılmaz. Ayrı staging ortamında üretime benzer indeksler, veri dağılımı, TLS ve log seviyesi kullanılır. Örnek kurulum: iki API instance’ı, her biri 2 vCPU/4 GiB; iki worker process’i; ayrı 4 vCPU/8 GiB PostgreSQL; bağımsız yük üreticisi; 5–50 ms ağ gecikmesi senaryoları. Bu donanımın hedefi karşıladığı henüz kanıtlanmış değildir.

Tekrarlanabilir yük testi protokolü
AşamaYük / süreKabul koşulu
Isınma50 request/s, 5 dakikaCache ve bağlantı havuzları dengelenir; ayrı raporlanır.
Normal50 request/s, 15 dakikaLatency dağılımı, hata sınıfları, lock wait ve maliyet toplanır.
Pik200 request/s, 20 dakikaKabul p95 ≤ 300 ms; teknik hata ≤ %0,1; artan backlog yok.
Stres400 request/s, 5 dakikaKapasite sınırı ve kontrollü ret gözlenir; doğruluk korunur.
Toparlanma50 request/s, 10 dakikaBacklog ≤ 5 dakikada normal seviyeye iner; kayıp iş yok.
Soak / fault50 request/s, 2 saat + ayrı fault koşularıBellek trendi, worker kill, DB failover ve sağlayıcı timeout’u incelenir.

Trafik karışımı %70 katalog/durum okuma, %20 yeni checkout, %10 aynı anahtarlı tekrar isteğidir. Pik 200 request/s iken yeni ödeme iş gelişi yaklaşık 40 iş/s olur. Sağlayıcı ortalama 500 ms’de yanıtlıyorsa 32 worker slot’unun teorik üst sınırı 64 iş/s’dir; kota, retry ve tail latency bunu düşürür. %70 kullanım hedefi yaklaşık 44,8 iş/s bırakır; 40 iş/s için marj dardır. Bu yüzden sağlayıcı kotası ve kuyruk yaşı ölçülmeden “yeterli” denmez.

Yük üreticisi sabit geliş hızını korumalıdır: sistem yavaşlayınca yeni istek göndermeyi de yavaşlatan kapalı model, coordinated omission nedeniyle gecikmeyi olduğundan iyi gösterebilir. Sunulan ve tamamlanan throughput, dropped iteration, timeout ve bütün HTTP sonuçları birlikte raporlanır. İlk örneklerin hızlı olması için rastgele SKU seçmek yetmez; %20 isteğin aynı popüler SKU’ya gittiği sıcak satır testi ve aynı idempotency anahtarını eşzamanlı kullanan test ayrıca yapılır.

Kanıt paketi uygulama commit’i, migration sürümü, test seed’i, yük script’i, ortam ayarları ve ham histogramları içermelidir. İyileştirme sırası ölçüme dayanır: N+1 sorguyu kaldırmak, (tenant_id, created_at) indeksini doğrulamak, sayfalama ve bağlantı havuzu sınırı koymak; sonra cache veya yatay ölçek. Connection pool için toplam bağlantı = instance × pool + worker + bakım rezervidir. Her process’e 100 bağlantı vermek DB kapasitesini büyütmez.

6. Production-Grade AI: LLM Entegrasyonu Neden Sadece API Çağrısı Değildir?

Aynı sistemde LLM’nin görevi geciken sipariş için müşteriye açıklama taslağı hazırlamak olsun. Model ödeme onayı vermez, stok düşmez ve iade başlatmaz. Siparişin gerçek durumu veritabanından ve yetkili servislerden gelir; model yalnız bu veriden bir taslak üretir. AI worker ödeme kuyruğundan ayrıdır; LLM servisi kapalıyken sipariş ve tahsilat çalışmaya devam eder.

JSON / example validated output
{
  "order_id": "O-104",
  "template": "payment_under_review",
  "draft": "Your payment status is being verified.",
  "evidence_refs": ["order:O-104:status:v7"],
  "requires_review": true
}

JSON parse edilebilmesi doğruluk kanıtı değildir. Şema doğrulamasından sonra order_id’nin istekle eşleşmesi, evidence_refs’in izinli kaynaklara dayanması ve metnin gerçek ödeme durumuyla çelişmemesi kontrol edilir. Modelin “requires_review: false” yazması yetki sağlamaz; dış politika motoru inceleme kararını verir. Kaynak belgeler tenant filtresinden geçirilir; retrieval sonrası da erişim kontrolü yapılır. Müşteri notu veya dokümandaki talimatlar güvenilmeyen veridir. OWASP prompt injection rehberi bu sınırın neden yalnız system prompt ile korunamayacağını açıklar.

Örnek bütçe: iş başına en fazla 2.000 input ve 400 output token, 8 saniye deadline, global eşzamanlılık sınırı ve tenant kotası. Timeout veya doğrulama hatasında yeniden kontrol edilen sabit şablon gösterilir; sonsuz “JSON düzelt” döngüsü çalışmaz. Model, prompt, retrieval ve şema sürümleri kaydedilir; ham kişisel veri loglanmaz. Test seti normal örneklerin yanında yanlış tenant belgesi, boş retrieval, çelişkili ödeme bilgisi ve “önce iade yap” enjeksiyonunu içermelidir.

AI için izlenen metrikler şema geçerlilik oranı, doğrulanmış kaynak kullanımı, insan kabul oranı, fallback oranı, p95 süre ve kabul edilen taslak başına maliyettir. Yanlış tenant verisi veya yetkisiz aksiyon denemesi tek olayda durdurma nedenidir; bunun için hata bütçesi tanınmaz. Yeni model sürümü önce kayıtlı veriyle regression değerlendirmesinden, sonra sınırlı canary’den geçer. Düşük token faturası, yanlış müşteriye gönderilen açıklamanın operasyonel bedelini telafi etmez.

7. Architectural Decision Records: Monolith, Microservices ve Gereksiz Karmaşıklık

ADR bir teknoloji alışveriş listesi değildir. Her kayıt bağlamı, değerlendirilen seçenekleri, kararı, kaybedilen avantajları, sorumluyu ve yeniden değerlendirme tetikleyicisini içermelidir. Ekibimiz dört geliştirici, bağımsız platform ekibi yok ve iki ayrı iş alanının farklı release temposu henüz kanıtlanmış değil varsayımıyla başlar. Bu koşullar teknoloji seçiminden daha belirleyicidir.

Senaryoya ait karar kayıtları
KararGerekçe / bedelYeniden değerlendirme
ADR-001: Modüler monolitStok + sipariş yerel transaction’da; tek deploy. Modül sınırı ve sahiplik disiplini gerekir.Bir modül bağımsız ölçek veya ayrı ekip/release gerektirirse.
ADR-002: Önce PostgreSQL outboxDüşük/orta olay hacmi, az tüketici; mevcut DB ile dayanıklı iş. Polling ve DB yükü ekler.Replay saklama, çok sayıda bağımsız tüketici veya sürdürülemeyen lag oluşursa Kafka değerlendir.
ADR-003: Redis koşulluKatalog cache’i ve rate limit için ölçülen ihtiyaç varsa; ödeme gerçeği için değil. Yeni servis işletme maliyeti var.DB okuma yükü / gecikme hedefi cache ile somut iyileşiyorsa ekle.
ADR-004: Şimdilik Kubernetes yokİki API ve worker için VM/container + systemd veya yönetilen servis yeterli olabilir. Otomasyon yine gerekir.Çok servis, sık bağımsız deploy ve platform ekibiyle toplam maliyet avantajı kanıtlanırsa.

Neden Redis? Yalnız gecikme veya DB yükü ölçümünde karşılığı varsa. Katalog için örneğin 30 saniye TTL ve değişiklikte invalidation kabul edilebilir; checkout yine PostgreSQL fiyat/stok doğrulaması yapar. Cache bozulduğunda toplu miss’in DB’yi çökertmesini önlemek için TTL jitter ve istek birleştirme uygulanır. Rate limit katmanı arızasında fail-open mı fail-closed mı davranılacağı endpoint’e göre tanımlanır; ödeme veya yönetim API’sinin güvenliği cache erişimine bırakılmaz.

Neden Kafka değil? Bu aşamada ihtiyaç birkaç worker’a iş dağıtmak; yüksek hacimli, uzun süre saklanan ve bağımsız tüketicilerce replay edilen olay günlüğü henüz gerekmiyor. Kafka yanlış teknoloji değildir; mevcut gereksinim için partition, şema, lag ve işletme sorumluluğunu eklemek gerekçelendirilmemiştir. Neden Kubernetes gerekli değil? Scheduler transaction doğruluğunu, sağlayıcı idempotency’sini veya backup geri dönüşünü tasarlamaz. Ekip yine health check, kontrollü rollout, secret yönetimi ve kapasite planını kurmalıdır.

Microservice’e ayrılma kararı verildiğinde “aynı veritabanına yazan ayrı container” ile bağımsızlık kazanılmaz. Veri sahipliği ve API sözleşmeleri ayrılmalı; servisler arası transaction’ın yerini mesajlaşma, saga ve mutabakat alır. Bu dönüşümün geliştirme ve incident maliyeti ADR’ye yazılır. Bir servis sınırı, yalnız kodu bölmek için değil bağımsız değişim veya hata izolasyonu için kurulmalıdır.

8. Software FinOps: Mimari Kararların TCO ve Operasyonel Maliyet Üzerindeki Etkisi

Bulut faturası TCO’nun yalnız bir bölümüdür. Compute, DB, depolama, egress, gözlemlenebilirlik ve model çağrıları doğrudan maliyettir; bakım, nöbet, incident ve değişiklik süresi mühendislik maliyetidir. Başlangıç geliştirmesi ayrıca amorti edilir. Aşağıdaki tablo teklif veya sağlayıcı fiyat listesi değildir; aynı hacimde iki tasarımı karşılaştırmak için hazırlanmış varsayımsal muhasebe modelidir.

Örnek aylık maliyet modeli — USD, vergi ve ödeme komisyonu hariç
KalemModüler monolitDaha erken dağıtılmış platform
Compute + DB + backup + ağ500900
Log / metric / trace100250
Opsiyonel AI4040
Bakım: varsayılan 50 USD/saat12 saat × 50 = 60032 saat × 50 = 1.600
Aylık toplam1.2402.790

Ayda 1 milyon uygun API isteğinde ilk tasarımın doğrudan servis maliyeti 640 / 1.000.000 = 0,00064 USD/request; bakım dahil maliyeti 0,00124 USD/request olur. 100 bin kabul edilen sipariş için toplam tahsis 0,0124 USD/sipariştir. Siparişlerin yalnız 90 bini başarıyla ödeniyorsa aynı toplamın başarılı sipariş başına maliyeti yaklaşık 0,01378 USD’ye çıkar. Payda ve kapsama ad verilmeden “request başına maliyet” yanıltıcıdır. Ödeme komisyonu, destek, incident ve ilk geliştirme amortismanı bu rakamlarda yoktur; TCO raporunda ayrıca eklenir.

Cost model / illustrative assumptions
# Hypothetical token rates; NOT a vendor price quote.
input_rate_per_million  = 1.00  # USD
output_rate_per_million = 4.00  # USD
cost_per_generation = (2000 * 1.00 + 400 * 4.00) / 1_000_000
# = 0.0036 USD

generations = 10_000
retry_multiplier = 1.10
monthly_model_cost = generations * retry_multiplier * cost_per_generation
# = 39.60 USD (~40 budgeted)

accepted_drafts = 8_000
model_cost_per_accepted_draft = 39.60 / accepted_drafts
# = 0.00495 USD; human review and retrieval are additional.

# TCO(period) = implementation + migration + recurring services
#             + engineering operations + incident impact + exit cost

Otomasyonun tasarrufu da net hesaplanır: işlem başına 3 dakika kazanç × ayda 100 bin işlem = 5.000 saat teorik kazançtır; gerçek kullanım oranı, istisna çözümü ve insan kontrolü bu kazançtan düşülür. Bir log satırına tüm response’u koymak depolamayı, sınırsız retry model faturasını, gereksiz servis sınırı nöbet yükünü büyütebilir. Örnek tabloda aylık fark 1.550 USD, yıllık tekrar eden fark 18.600 USD’dir; bu evrensel bir microservice maliyet oranı değildir.

Bütçe alarmı tenant ve iş yeteneği bazında çalışır: kabul edilen sipariş, başarılı tahsilat ve kabul edilen AI taslağı başına maliyet izlenir. Günlük limit dolduğunda önce opsiyonel AI azaltılır; ödeme doğruluğu veya audit kaydı kapatılmaz. Trafik yarıya düştüğünde sabit maliyet nedeniyle birim maliyetin yaklaşık iki katına çıkabileceği de kapasite kararına yazılır. Birim ekonomi yaklaşımı için FinOps Foundation — Unit Economics iyi bir başvuru kaynağıdır.

9. Production Readiness Checklist: Bir Sistem Ne Zaman Canlıya Alınabilir?

“Testler yeşil” tek başına go-live kararı değildir. Kabul maddelerinin kanıtı, sahibi ve başarısızlık halinde uygulanacak eylemi olmalıdır. Aşağıdaki kapılar bu sipariş/ödeme senaryosu içindir; sağlık, finans veya farklı veri sınıfları ek gereksinimler doğurabilir. Kritik doğruluk ve yetki ihlalleri için “sonra düzeltiriz” istisnası verilmez.

Canlıya geçiş için kanıt kapıları
KapıGösterilecek kanıtSorumlu
Veri bütünlüğüAynı anahtarla 100 eşzamanlı çağrı → tek sipariş; son stokta yarış → tek rezervasyon.Backend
Ödeme telafisiTahsilat sonrası worker kill, duplicate/out-of-order webhook ve refund timeout provası.Ödeme entegrasyonu
GüvenlikTenant A, Tenant B siparişine erişemez; rol testleri, secret sızıntı taraması, webhook imza/replay kontrolü.Güvenlik / backend
YükSürüm ve ortamı kayıtlı pik testi, p95/hata/throughput, sıcak SKU ve recovery sonucu.Performans / SRE
KurtarmaAyrı ortamda backup restore, PITR ve failover provası; RPO/RTO ölçümü.Operasyon
DeployGeri uyumlu migration, canary, trafik boşaltma, rollback provası.Release sahibi
İzleme ve bütçeUçtan uca sentetik sipariş, çalışan alarm rotası, maliyet dashboard’u ve limitler.SRE / ürün
AI izolasyonuLLM kapalıyken ödeme çalışır; tenant ve yetkisiz eylem testleri geçer.AI / backend

Güvenlik ayrıntıları iş akışında doğrulanır: tenant kimliği payload’dan değil doğrulanmış oturumdan alınır; order_id bilinmesi erişim yetkisi sağlamaz. DB kullanıcısı en az yetkili rol ile çalışır; secret’lar kodda tutulmaz ve rotasyon denenir. Kart verisi uygulama sunucusundan geçirilmeden sağlayıcının tokenizasyon/hosted checkout akışı tercih edilir. Webhook ham gövdesi üzerinden imza ve zaman penceresi doğrulanır, provider_event_id kalıcı tekillikle kaydedilir; 2xx ancak event dayanıklı kayda alındıktan sonra verilir. Gecikmiş olayın daha yeni PAID durumunu geriye çevirmesine izin verilmez. Sağlayıcı ayrıntıları için webhook dokümantasyonuna bakılmalıdır.

Örnek kurtarma hedefi DB için RPO ≤ 5 dakika ve hizmet için RTO ≤ 30 dakikadır; bunlar restore tatbikatıyla doğrulanana kadar hedeftir. Yedeklenmiş DB’den daha güncel bir tahsilat sağlayıcıda var olabilir. Recovery sonrası müşteri trafiği açılmadan belirsiz ödemeler ve rezervasyonlar sağlayıcı kayıtlarıyla uzlaştırılır. Yalnız nightly backup ile beş dakikalık RPO vaat edilemez; sürekli arşivleme/PITR ve izole kurtarma pratiği gerekir. Backup’ın tamamlanması, geri yüklemenin başarılı olduğu anlamına gelmez.

Deploy için expand–migrate–contract yaklaşımı kullanılır: önce eski ve yeni kodun okuyabildiği şema eklenir, veri arka planda taşınır, yeni okuma yolu açılır, eski alan daha sonraki sürümde kaldırılır. Destructive migration sonrası eski binary’ye dönmek güvenli olmayabilir. Worker SIGTERM aldığında yeni iş almayı bırakır; devam eden işi deadline içinde tamamlar veya lease’in kontrollü sona ermesine izin verir. Canary sırasında SLO, ödeme tutarsızlığı ve maliyet artışı birlikte izlenir.

Ürün sahibi kabul edilen riskleri, teknik sorumlu doğruluk kanıtını, operasyon sorumlusu kurtarma ve alarm provasını imzalar. Eksik kanıt varsa trafik açılışı ertelenir veya kapsam açıkça azaltılır; örneğin AI kapalı kalır. Production-ready olmak belirli bir ürün, yük ve hata modeli için kanıtlanmış bir sözleşmedir; sonsuza kadar kazanılmış bir etiket değildir.

Biz yalnız yazılımın çalışmasını sağlamıyoruz. Hangi yükte, hangi hata koşullarında, hangi veri garantileriyle ve hangi mühendislik maliyetiyle güvenilir çalışacağını tasarlıyoruz.

Kendi sisteminiz için başlangıç noktası yeni bir teknoloji listesi değil; tek bir kritik akışın invariant’larını, SLO’sunu, hata telafisini ve birim maliyetini birlikte çıkarmaktır. Mühendislik ihtiyacınızı konuşalım. İlgili teknik okumalar: sistem entegrasyonu ve modern kurumsal yazılım mimarisi.

Sık Sorulan Sorular

01PoC kodu production’a taşınabilir mi?

Evet, fakat çalışan demo tek başına yeterli değildir. Veri constraint’leri, idempotency, yetki sınırları, hata telafisi, gözlemlenebilirlik ve restore/deploy kanıtı tamamlanmalıdır. Gerektiğinde yalnız riskli bileşenler yeniden yazılır.

02MVP’de güvenilirlik ertelenebilir mi?

Gerçek para ve müşteri verisi işleniyorsa temel doğruluk ve güvenlik ertelenmez. MVP’de özellik kapsamı azaltılır; çift tahsilat veya tenant veri sızıntısı kabul edilmez.

03Ödeme timeout verirse tekrar tahsilat yapılır mı?

Yeni anahtarla kör tekrar yapılmaz. Aynı kalıcı payment attempt ve sağlayıcı anahtarı korunur; sağlayıcı sözleşmesine göre güvenli tekrar, durum sorgusu veya mutabakat uygulanır. Timeout başarısız ödeme kanıtı değildir.

04Idempotency için Redis yeterli mi?

Ödeme ve siparişin kalıcı tekilliği için tek başına yeterli değildir. PostgreSQL unique constraint ve saklanan yanıt kullanılır. Redis ölçülmüş cache veya rate-limit ihtiyacına hizmet edebilir.

05Outbox exactly-once garantisi verir mi?

Hayır. Outbox, yerel veri değişikliği ile olay kaydını atomik tutar; teslim ve işleme tekrarlanabilir. Tek iş etkisi için tüketici tekilleştirmesi, durum makinesi ve sağlayıcı idempotency’si gerekir.

06Kafka ve Kubernetes ne zaman gerekir?

Bağımsız tüketici ve replay ihtiyacı, olay hacmi, servis sayısı, release sıklığı ve ekip kapasitesi mevcut çözümün sınırını aştığında değerlendirilir. Sırf production etiketi için gerekli değildir.

07p95 latency ile SLO arasındaki fark nedir?

p95 bir gecikme dağılımı ölçüsüdür; isteklerin yüzde 95’i bu değerde veya altında tamamlanır. SLO ise belirli payda ve zaman penceresinde hedeflenen hizmet seviyesidir. API kabulü ile ödeme sonuçlanması ayrı ölçülmelidir.

08LLM’ye ödeme veya iade yetkisi verilmeli mi?

Bu senaryoda hayır. Model açıklama taslağı üretir; ödeme ve iade kararlarını yetkili, deterministik iş kuralları verir. Şema doğrulaması tek başına yetki veya doğruluk sağlamaz.

09Yazıdaki performans ve maliyet sayıları gerçek test sonucu mu?

Hayır. Rakamlar açıkça tanımlanmış referans varsayımları, kabul hedefleri ve örnek maliyet hesaplarıdır. Gerçek kapasite için sürümü ve ortamı kayıtlı yük testi; gerçek maliyet için kullanım ve fatura verisi gerekir.

10Bir sistemin canlıya hazır olduğuna kim karar verir?

Ürün, teknik ve operasyon sorumluları kanıta dayanarak birlikte karar verir. Doğruluk, güvenlik, yük, kurtarma, rollback ve alarm kapıları geçmeden kritik trafik açılmaz.

İlgili notlar

WhatsAppDoğrudan iletişim