Skip to content
Founder @dakicksoft edited this page Aug 17, 2026 · 2 revisions

Mimari

Dört ilke

# İlke Anlamı
1 Modüler monolit Türkiye'nin tüm kartlı e-ticaret hacmi tek Postgres'e sığar. Modüller yalnız *.Contracts üzerinden konuşur; sınır mimari testle (NetArchTest) zorlanır.
2 Offline-first istemci, sunucu otoritesi Saha cihazı ağsızken kayıt kuyruklar; ama yasal işlem zamanı yalnız sunucu saatinden yazılır. Cihaz "tahsil edildi" üretemez — yalnız "tahsilat talebi" üretir.
3 Hiçbir operasyonel kayıt silinmez Olay defterlerinde DELETE (çoğunda UPDATE de) hakkı uygulama rolünde yoktur — GRANT düzeyinde. İptal = yeni durum + yeni olay. KVKK silme bir UPDATE'tir; mali kayıt yerinde kalır.
4 İki katmanlı işyeri izolasyonu Katman A: EF global query filter. Katman B: Postgres RLS (poyra_app rolü NOBYPASSRLS, açılışta canlı doğrulanır). RLS testleri ham SQL ile bile işyerleri arası görünmezliği kanıtlar.

Üç host, tek veritabanı

flowchart LR
    subgraph Istemciler["İstemciler"]
        M["İşyeri sunucusu<br/>(REST + webhook)"]
        P["İşyeri personeli<br/>(tarayıcı)"]
        C["Son müşteri<br/>(tarayıcı)"]
        S["Saha temsilcisi<br/>(MAUI/Android)"]
    end

    subgraph Poyra["Poyra (modüler monolit)"]
        API["Poyra.Api<br/>REST + callback + Hangfire"]
        PANEL["Poyra.Panel<br/>Blazor panel"]
        CO["Poyra.Checkout<br/>ödeme sayfası"]
        MOD["Modules/*<br/>CQRS dispatcher üzerinden"]
        CONN["Connectors/*<br/>banka & PSP adaptörleri"]
    end

    DB[("PostgreSQL 18<br/>RLS + append-only")]
    BANK["Bankalar / PSP'ler<br/>(sanal POS, 3DS)"]

    M -->|X-Api-Key| API
    P --> PANEL
    C --> CO
    S -->|toplu senkron| API
    API --> MOD
    PANEL --> MOD
    CO --> MOD
    MOD --> DB
    MOD --> CONN
    CONN <--> BANK
    BANK -->|"callback (tek kullanımlık belirteç)"| API
    API -.->|"HMAC imzalı webhook"| M
    DB -.->|LISTEN/NOTIFY| PANEL
Loading

Sık sorulan üç tasarım sorusu:

  • Panel neden API'ye HTTP atmaz? Modüler monolit avantajı: Panel aynı süreçte aynı komut/sorgu yolunu (dispatcher) doğrudan çağırır — çift serileştirme ve ek gecikme yok.
  • Checkout neden ayrı host? Kimliksiz, herkese açık ve saldırı yüzeyi en geniş bileşen; panelden ve API'den ayrı ölçeklenir/kapatılır.
  • Kart verisi nerede? Hosted akışta hiç uğramaz (banka sayfası). Direct/token akışı için Kasa modülü: PAN AES-256-GCM zarfta, CVV asla saklanmaz.

Çözüm yapısı

poyra/
├─ src/
│  ├─ Poyra.SharedKernel        Elle yazılmış CQRS dispatcher, TenantContext/UserContext,
│  │                            IClock, TurkishText, para/giriş yardımcıları, sır şifreleme
│  ├─ Poyra.Persistence         ModuleDbContext tabanı, RLS + audit interceptor'ları
│  ├─ Connectors/               Abstractions + 21 banka/PSP adaptörü
│  ├─ Modules/                  20 iş modülü — her biri kendi DbContext + migration geçmişiyle
│  ├─ Poyra.Api                 REST API (FastEndpoints), banka callback'leri, Hangfire, Scalar, OTel
│  ├─ Poyra.Panel               İşyeri paneli (Blazor, statik SSR + interaktif adalar)
│  ├─ Poyra.Checkout            Müşteriye bakan kimliksiz ödeme sayfası (açık tema)
│  ├─ Poyra.Field.Core          Saha kuyruk/senkron mantığı (sade, testli kütüphane)
│  └─ Poyra.Field.App           MAUI/Android saha uygulaması (ince kabuk)
├─ tests/                       Unit · Architecture (modül sınırı + PCI bekçileri) · Integration
├─ docker/ + docker-compose*.yml
└─ docs/                        Ürün ve entegrasyon dokümanları

Test piramidi

1.111 test, dört katman:

  • 719 birim — konnektör hash'leri, TOTP RFC 6238 vektörleri, taksit matematiği, EMV QR, yeniden tahsilat politikası ve konnektör uyum kiti.
  • 37 mimari/PCI bekçisi — modül sınırı, CVV sütunu yokluğu, düz PAN taraması.
  • 334 entegrasyon — gerçek PG 18 + gerçek HTTP webhook alıcısı + panel/checkout HTML doğrulaması + LISTEN/NOTIFY.
  • 21 E2E — gerçek tarayıcı (Playwright): interaktif adalar, TOTP 2FA yolculuğu, tehlikeli aksiyon onayı, mobil çekmece, yazdırma stilleri.

Kapsam: satır %80,7 · dal %54,3 · metot %90. Kapsam kapısı bir hedef değil gerileme kapısıdır: testsiz kod eklenince oran düşer ve PR kırılır. Ayrıca Stryker.NET ile mutasyon testi elle tetiklenir (./scripts/mutasyon.sh).

Clone this wiki locally