Fan Yemek API

REST endpoint referansı

Sistem Mimarisi

Çok-tenant (SaaS) marketplace: müşteri, satıcı ve system istemcilerinden core servislere uzanan yapı; kurye dış sağlayıcı connector ile

Yemek sipariş uygulaması sistem mimarisi diyagramı

Mimari Analiz & Ürün Yönü

Ekibe ürünün nasıl ilerleyeceğini aktaran kararlar ve standartlar

0 · Genel Yaklaşım

Bu proje yalnızca backend kapsamındadır. Müşteri App ve Satıcı Panel gibi frontend'ler ayrı değerlendirilecek; kurye operasyonu dış sağlayıcılara (KuryeMaxi vb.) devredilir. Bu referans REST API sözleşmesini ve mimari kararları tanımlar.

Hedef, yüksek trafikli bir marketplace (Yemeksepeti/Getir muadili) backend'idir. Üç aktör vardır: customer user (satıcı sahibi · temsilci) system (super_admin · ops · finance · content · support) — her biri ayrı API bölümünde ve kendi kimlik/yetki katmanında.

SaaS / Çok-Tenant model

Fan Yemek App tek bir marketplace değil, SaaS platformudur: altında birden çok bağımsız marketplace/brand (Tenant — ör. Fan Yemek, Gıs Yemek, Ts Yemek) yayınlanır. Tenant'ları platform ekibi (system) oluşturup yönetir; her tenant kendi host/branding'ine sahiptir.

  • Satıcılar global bir hesaptır; hangi marketplace'te yayına çıkmak istiyorsa başvuru gönderir (SellerTenant), system onaylar. Komisyon oranı tenant bazlıdır (SellerTenant.commissionRate, varsayılan Tenant.defaultCommissionRate).
  • Her tenant kendi ödeme altyapısını tahsilat-app üzerinden tanımlar (TenantPaymentConfig); online tahsilat tenant merchant ref ile yönlendirilir.
  • Satıcı menüleri MenuDefinition olarak tanımlanır; onay sonrası istenen tenant(lar)da yayına alınır (MenuDefinitionPublication — çoka-çok).
  • Ürünler dinamik kategoriden bağımsız, sabit bir MenuType (menü türü) ile sınıflandırılır — mağaza içeriğini belirlemek için. Menü hiyerarşisi: Kategori → Ürün.

Detaylı kapsam karşılaştırması ve "neden ayrı uygulama yazmalıyız" gerekçeleri için bkz. fan-yemek-kapsam-analizi.md.

1 · Aktörler & Rol Yönetimi (RBAC Best Practices)

Aktör = API bölümü; rol = aktör içindeki yetki katmanı. seller aktörü satıcı (Seller) sahibi (owner) ve temsilci rollerini barındırır. system aktörü super_admin, ops, finance, content ve support rollerini barındırır — bir system kullanıcısı birden fazla role sahip olabilir. tenant aktörü tenant'a ait salt-okunur raporlama kullanıcılarıdır (kendi tenant kapsamı). Kurye operasyonu dahili uygulama değil; dış sağlayıcı connector ile yürütülür.

Tenant kapsamı: system aktörü, platform genelinde marketplace/tenant'ları ve MenuType katalogunu yönetir. Müşteri istekleri aktif tenant kapsamındadır (host veya X-Tenant ile çözümlenir). Satıcının bir tenant'ta görünmesi için önce yayın başvurusunun, menüsünün görünmesi için menü yayınının system onayından geçmesi gerekir. Tenant aktörü yalnız kendi tenant'ının raporlarını salt-okunur görür.

Aktör / Rol Prefix Erişebildiği başlıca alanlar
customer /v1 Keşif, sepet, sipariş, ödeme, profil — yalnızca kendi verisi (telefon OTP ile giriş/kayıt)
seller · owner /v1/seller Tam satıcı (Seller) yönetimi: profil, menü, sipariş, analiz, finans, teslimat dispatch (telefon OTP zorunlu)
seller · temsilci /v1/seller Yalnızca sipariş operasyonu (durum güncelleme); menü/finans/profil kapalı (telefon OTP zorunlu)
tenant · reporting /v1/tenant-panel Yalnız kendi tenant'ına ait salt-okunur raporlar/dashboard (sipariş, ciro, satıcı performansı, komisyon özeti); yazma yok (telefon OTP zorunlu)
system · super_admin /v1/system Tam platform erişimi: tüm endpoint'ler, sistem config yazma, audit-log (telefon OTP zorunlu)
system · ops /v1/system Operasyon: kullanıcı/satıcı yönetimi, onay/askıya alma, kurye sağlayıcıları, sipariş izleme, audit-log okuma
system · finance /v1/system Finans: iade, payout, komisyon güncelleme, finans özeti, satıcı/sipariş salt okunur
system · content /v1/system İçerik: mutfak kategorileri CRUD, kupon/kampanya, toplu bildirim
system · support /v1/system Destek: kullanıcı/sipariş görüntüleme, hesap askıya alma — GDPR silme ve config yazma kapalı

Best practice özeti

  • Aktör başına ayrı token/scope: customer token'ı panel uçlarına geçemez. Access JWT claim'leri: act (customer | seller | tenant | system), roles[], satıcı için sellerId, tenant için tid, oturum için sid / jti / ver, hedef API için aud. Detaylı izinler token'da değil; rol → permission map sunucuda türetilir.
  • En az ayrıcalık (least privilege): her rol yalnızca ihtiyaç duyduğu yetkiye sahip. Yeni rol eklenebilir olmalı (rol → izin eşlemesi konfigüratif). system aktörü çoklu rol destekler; super_admin implicit olarak tüm system endpoint'lerine erişir.
  • Endpoint düzeyinde policy: yetki kontrolü gateway/filter + servis katmanında çift kademeli; ayrıca resource ownership kontrolü (satıcı X'in temsilcisi sadece X'in siparişlerini görür).
  • Kısa ömürlü access + refresh token rotation; anında iptal için ver + Redis INCR auth:ver:{sub} birincil mekanizma; blacklist:access:{jti} yalnızca opsiyonel (tek cihaz logout, kalan access TTL kadar).
  • Telefon OTP zorunlu (customer, seller, tenant, system): giriş ve kayıt phoneNumber ile; varsayılan kanal WhatsApp (app.monochat.ai), alternatif SMS (Twilio). TOTP/authenticator app kullanılmaz.
  • Birincil kimlik: phoneNumber + actor birlikte unique — aynı numara farklı panellerde ayrı hesap olabilir.
  • system aktörü hassas işlemleri audit-log'a yazılır.

Not: API ağacında ilgili uçlarda Gerekli rol rozetiyle hangi rolün erişebildiği işaretlenmiştir.

1b · JWT & Oturum Mimarisi (OTP → Token Döngüsü)

Klasik şifre login yok; OTP verify = login. Akış: otp/send → otp/verify → accessToken + refreshToken → refresh (rotation) → logout. Access token kısa ömürlü JWT (RS256/ES256, 10–15 dk); refresh token uzun ömürlü opaque string (7–30 gün, Redis'te saklanır).

Token modeli

Token Format Ömür Taşıma
accessToken JWT 10–15 dk Authorization: Bearer
refreshToken Opaque 7–30 gün HttpOnly cookie (web) / secure storage (mobil)

Access token claim şeması

{
  "sub": "user-uuid",
  "act": "customer | user | system",
  "roles": ["owner"],
  "sellerId": "seller-uuid",
  "sid": "session-family-uuid",
  "jti": "access-token-uuid",
  "ver": 3,
  "iss": "fan-yemek-auth",
  "aud": "customer-api | seller-api | system-api",
  "iat": 1710000000,
  "exp": 1710000900
}
Aktör Zorunlu claim'ler
customer sub, act, sid, jti, ver, aud
seller + roles (owner | representative), + sellerId
tenant + tid (tenant kapsamı) — salt-okunur raporlama
system + roles[] (super_admin, ops, finance, content, support)

JWT'ye koy / koyma

Bilgi JWT'de? Neden
sub, act, roles, sellerId Evet Authorization
sid, jti, ver Evet Session / anında iptal — ver Redis auth:ver:{sub} ile eşleşmeli
Telefon, ad, e-posta Hayır PII — verify response / /users/me
Tenant Hayır Host / X-Tenant ile çözülür
Detaylı permissions Hayır Rol → izin map sunucuda türetilir

OTP + JWT akışı

Yetkilendirme katmanları

  • Gateway/filter: JWT imza + TTL doğrula; act + aud route prefix ile eşleşsin; ardından ver Redis kontrolü (endpoint policy'sine göre fail-open / fail-closed).
  • Servis katmanı: rol kontrolü + resource ownership (sellerId === order.sellerId).
  • Aktör izolasyonu: customer token /v1/seller/* ve /v1/system/* uçlarına geçemez.

İptal mekanizmaları

Mekanizma Ne zaman Nasıl
ver (Redis version) Rol değişimi, askıya alma, force logout, refresh reuse sonrası oturum iptali INCR auth:ver:{sub} — tüm cihaz/sekmeler anında düşer
Refresh session Logout, rotation, reuse detection Mevcut refresh:{tokenHash} + sid ailesi
Access TTL Her istek 10–15 dk; imza doğrulaması Redis'e gitmeden yapılır
Blacklist (opsiyonel) Tek cihaz logout blacklist:access:{jti} TTL = kalan access süresi

Redis aynı VPS/local ağda round-trip ~1 ms; tek INCR ile blacklist'e göre çok daha az operasyon. OTP verify / refresh sırasında GET auth:ver:{sub} (yoksa 0) → JWT ver claim'ine yazılır.

Access token doğrulama (gateway middleware)

async function verifyAccessToken(req, policy = 'fail-open') {
  const decoded = jwt.verify(req.token, PUBLIC_KEY, { aud, iss });

  try {
    const current = await redis.get(`auth:ver:${decoded.sub}`);
    if (current !== null && parseInt(current, 10) !== decoded.ver) {
      throw unauthorized('TOKEN_STALE');
    }
  } catch (err) {
    if (err.code === 'TOKEN_STALE') throw err;
    // Redis erişilemez
    if (policy === 'fail-closed') {
      throw serviceUnavailable('AUTH_STORE_UNAVAILABLE');
    }
    // fail-open: log + devam
  }

  return decoded;
}

Korunan tüm route'larda standart auth hataları: 401 TOKEN_STALE (eski ver), 503 AUTH_STORE_UNAVAILABLE (fail-closed route + Redis down).

Redis erişilemezlik politikası

Politika Ne zaman Redis down davranışı
fail-open (varsayılan) Müşteri keşif, sepet, sipariş okuma, satıcı panel okuma İstek devam eder; kısa pencerede eski ver kabul edilir
fail-closed /v1/system/* yazma, ödeme/checkout, hesap askıya alma, rol atama 503 AUTH_STORE_UNAVAILABLE — güvenlik öncelikli

Global tek davranış yerine route metadata / guard seviyesinde securityPolicy: 'strict' | 'standard' ile ayrılır.

Redis anahtarları

  • otp:{sessionId} → OtpChallenge (mevcut)
  • auth:ver:{sub} → integer counter (TTL yok; kalıcı, ~8 byte/kullanıcı)
  • refresh:{tokenHash}{ sub, act, sid, ver, expiresAt }
  • blacklist:access:{jti} → opsiyonel, ver yaklaşımına göre ikincil; logout sonrası kalan access TTL kadar

1c · Integration API (3. Parti — Faz 2+)

Faz 1 kapsamı dışı

Integration API Faz 1'de implement edilmez; partner onboarding ve dış istemci erişimi yoktur. Bu bölüm ilerideki entegrasyonlar (ör. resto-maxi-app kanal bağlantısı) için tasarım notudur.

3. parti uygulamalar (POS, entegratör, ERP) makine-to-machine istemcidir. OTP veya kullanıcı oturumu kullanılmaz; kimlik integration client credentials (clientId + clientSecret) ile doğrulanır. Inbound webhook'lar (/webhooks/*, HMAC imza) bu bölümden ayrıdır — bkz. Platform Webhook / Kurye Connector.

Kimlik akışı (apiKey → JWT)

clientId + clientSecret → token → Bearer JWT → (refresh rotation)

  1. System/partner onboarding sonrası entegratöre clientId + clientSecret verilir (secret hash'lenerek saklanır).
  2. POST /v1/integration/auth/token — credential exchange.
  3. Yanıt: accessToken (JWT) + refreshToken (opaque) — Bölüm 1b'deki rotation mantığı geçerlidir.
  4. Sonraki istekler: Authorization: Bearer {accessToken}.
  5. Süresi dolunca: POST /v1/integration/auth/refresh (Faz 2+).

Access JWT claim'leri: act=integration, aud=integration-api, scopes[]; isteğe bağlı sellerId / tenantId (client kaydına bağlı kapsam). OTP/telefon yok.

Token alma — endpoint (Faz 2+)

Ham clientSecret yalnızca token exchange'de gönderilir; her API isteğinde taşınmaz.

POST /v1/integration/auth/token
Body: { "clientId": "...", "clientSecret": "..." }

200 OK:
{
  "accessToken": "...",
  "refreshToken": "...",
  "expiresIn": 900,
  "tokenType": "Bearer"
}

401 — Geçersiz credential
403 — Client askıda / süresi dolmuş
429 — Rate limit

2 · Faz Planı

Faz 1 — İlk öncelik

Sağlıklı bir Müşteri App (mobil/web) ve Satıcı Panel (mobil/web) altyapısı. İçerik: kimlik + RBAC, satıcı/menü katalog, keşif & arama, sepet, checkout, sipariş + statü makinesi, satıcı sipariş yönetimi ve tahsilat-app üzerinden ödeme akışı.

Sonraki fazlar (öneri)

  • Faz 2 — Kurye Connector & Tracking: dış kurye sağlayıcı entegrasyonu (KuryeMaxi, MuditaKurye vb.), outbound createDelivery + inbound webhook, müşteri teslimat takibi (normalize statü + trackingUrl).
  • Faz 3 — System & Finans: kullanıcı/satıcı yönetimi, kurye sağlayıcı konfigürasyonu, komisyon/payout, iade, kupon/kampanya, broadcast, tenant kullanıcı yönetimi + tenant raporlama paneli (salt-okunur). Faz 3 öncesi: nestjs-accountant POC (Bölüm 10); başarısızsa custom ledger.
  • Faz 4 — Gözlemlenebilirlik & ölçek sertleştirme: tracing/metrics/logs, arama (Elasticsearch) ve event akışlarının (Kafka/RabbitMQ) yaygınlaştırılması.

Tam faz dökümü için: fan-yemek-kapsam-analizi.md → Bölüm 7.

3 · Entegrasyonlar

Ödeme — tahsilat-app (tenant bazlı)

Tüm online tahsilat tahsilat-app üzerinden alınır; her tenant kendi merchant/APM tanımını tahsilat-app'te yapar (TenantPaymentConfig). Fan Yemek credentials tutmaz — yalnızca tenantId → tahsilatMerchantRef ile yönlendirir. Kapıda nakit/yemek kartı tahsilat-app'ten geçmez.

Kurye — Dış Sağlayıcı Connector

Kurye operasyonu proje dışında yürütülür (KuryeMaxi, MuditaKurye, Paketiniz vb.). Fan Yemek yalnızca gönder / al modeli kullanır: sipariş ready olunca connector createDelivery çağırır; sağlayıcı webhook ile durum bildirir. Dahili Kurye App yoktur.

Kimlik — MonoChat WhatsApp

Tüm aktörler (customer, seller, tenant, system) için OTP gönderiminin varsayılan kanalı. Fan Yemek auth servisi app.monochat.ai üzerinden WhatsApp OTP tetikler; OTP challenge Redis'te TTL ile tutulur.

Kimlik — Twilio SMS

Kullanıcı channel: sms seçerse OTP Twilio üzerinden SMS olarak gönderilir. WhatsApp ve SMS aynı OTP akışını paylaşır; yalnızca iletim kanalı değişir.

Finans defteri — double-entry ledger

Komisyon split, satıcı hakediş birikimi ve iade kayıtları ledger katmanında tutulur (tahsilat-app yalnızca kart/APM tahsilatı). Birincil aday: @vynelix/nestjs-accountant; yedek: custom TypeORM ledger. Detay: Bölüm 10 · Billing & Ledger Kararı.

4 · Ölçeklenebilirlik (yüksek trafik)

Proje yoğun trafik bekliyor; servisler yatayda (paralel) ölçeklenebilir olmalı: stateless uygulama instance'ları, oturum/cache dışarıda (Redis), uzun işler kuyruğa. Aşağıdaki yapılar temelde tasarıma dahil edilmeli:

Redis

Cache, oturum/token, sepet, teslimat durumu, rate limit sayaçları.

Kafka / RabbitMQ

Sipariş event'leri, bildirim ve tahsilat işlemlerinin asenkron işlenmesi, servis ayrıştırma.

Elasticsearch

Satıcı/menü arama ve q serbest aramasının düşük gecikmeyle karşılanması.

Başlangıçta net modül sınırlarına sahip bir modüler monolit; trafik arttıkça kritik modüller (sipariş, arama, teslimat) bağımsız servislere ayrılabilir. Geospatial sorgular için PostgreSQL + PostGIS.

5 · API Arayüz Standartları

Filtreleme — her zaman queryParam

Filtreler gövdede değil, daima query parametresi olarak gelir. q tüm tablolar için ön tanımlı alanlarda serbest arama yapar.

GET /api/v1/products?q=makarna
GET /api/v1/orders?orderDate.gt=100&orderDate.lt=200
GET /api/v1/sellers?rating.gte=4&sort=-rating&page=2&limit=20

Operatör eki sözleşmesi: .gt .gte .lt .lte .in .ne · sıralama: sort=alan / sort=-alan (azalan).

Liste yanıtı — standart pagination zarfı

Tüm list endpoint'leri sayfalama bilgisini veri içinde döner:

{
  "results": [ /* ... */ ],
  "options": {
    "paginations": {
      "currentPage": 1,
      "pageItems": 10,
      "totalCount": 100,
      "totalPage": 10
    }
  }
}

Hata yanıtı — tutarlı format

Tüm hatalar aynı zarfta döner; istemciler tek bir şemaya göre işler:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Geçersiz istek",
    "details": [ { "field": "phoneNumber", "message": "Zorunlu" } ]
  }
}

Standart HTTP kodları kullanılır (2xx başarı, 4xx istemci, 5xx sunucu). Aynı standart her iki katmanda (customer ve seller/satıcı) ortak uygulanır.

6 · Kategori Modeli (Rakip Analizi & Karar)

"Kategori" birden fazla kavram için kullanılıyor. Fan Yemek'te bunlar ayrı domain entity olarak tutulur; ek olarak dinamik kategoriden bağımsız, platform yönetimli sabit bir menü türü (MenuType) sınıflandırması vardır.

Katman Entity Amaç Kim yönetir? Model
Platform CuisineCategory Keşif, filtre, ana sayfa ikonları System System yönetimli katalog — satıcı serbest metin giremez
Platform MenuType Ürün sınıflandırma (mağaza içeriği) — dinamik kategoriden bağımsız System System yönetimli sabit lookup — satıcı üründe seçer
Satıcı MenuCategory Menü bölümleme (tek seviye kategori) Satıcı sahibi Tam dinamik — düz liste, parentId yok

Rakip platform özeti

Yemeksepeti

Başvuruda mutfak türü seçilir; sonradan destek talebiyle değişir. Müşteri app'te mutfak filtresi. Market tarafında parent/child hiyerarşi var; satıcı mutfağı pratikte düz liste.

GetirYemek

30+ mutfak kategorisi. Partner API'de /cuisines CRUD + görünürlük flag'leri + satıcı toplu atama. Menü kategorileri satıcı panelinde dinamik.

MigrosYemek

Başvuruda mutfak türü; store modelinde embeddedCuisine. Menü kategori/fiyat satıcı panelinden girilir.

Fan Yemek kararı — Hibrit model

  • CuisineCategory: system CRUD, sıralama, ikon/görsel, aktif/pasif, görünürlük flag'leri. Kodda hardcoded enum yok — seed migration ile başlangıç.
  • Satıcı ↔ cuisine çoklu ilişki (örn. Pizza + İtalyan); onay akışında system atar.
  • MenuType: system yönetimli sabit lookup (CuisineCategory ekranına paralel CRUD). Ürünü dinamik kategoriden bağımsız sınıflandırır (mağaza içeriğini belirler); satıcı üründe zorunlu seçer.
  • MenuDefinition: satıcı menülerini tanım olarak kurar (Kategori → Ürün) ve onay sonrası istediği tenant(lar)da yayına alır (MenuDefinitionPublication, çoka-çok). MenuCategory tek seviyeli düz listedir — iç içe kategori yok; platform system yalnızca yayını onaylar.

Önerilen seed mutfak listesi (~30)

İlk deploy'da migration ile yüklenir; system panelden ekleme/çıkarma mümkün.

Döner Burger Pizza Kebap Lahmacun & Pide Ev Yemekleri Çiğ Köfte Tatlı & Pastane Kahve & İçecek Dünya Mutfağı Deniz Ürünleri Vegan & Vejetaryen Fast Food Tost & Sandviç Mantı & Makarna Köfte Balık Börek Salata & Bowl Sokak Lezzetleri

System ekran taslakları

Ekran 1 — Mutfak Kategorileri Listesi
⋮⋮ İkon Ad Slug Satıcı Sıra Görünürlük Durum
⋮⋮ 🍕 Pizza pizza 842 1 Keşif Başvuru Aktif Düzenle
⋮⋮ 🥙 Döner doner 1.204 2 Keşif Başvuru Detay Aktif Düzenle
⋮⋮ 🍰 Tatlı tatli 318 8 Keşif Pasif Düzenle
Ekran 2 — Mutfak Düzenle / Oluştur
Pizza
Pizza
pizza
1
İkon yükle (64×64)
Banner görseli (400×200)

Önizleme — Müşteri app

🍕Pizza842 satıcı
Ekran 3 — Satıcı Onay + Mutfak Atama

Bekleyen: Lezzet Durağı — Kadıköy

Başvuru: 28.06.2026 · Sahip: ahmet@example.com

Ev Yemekleri × Çorba × + Mutfak ekle…

Onay sırasında PUT /sellers/:id/cuisines ile en az bir mutfak atanması zorunludur.

Menü kategorisi (MenuCategory) yönetimi satıcı panelinde kalır — system ekranına eklenmez (rakiplerle uyumlu).

7 · Teknoloji Önerisi

Karar özeti

Fan Yemek backend'i TypeScript + NestJS (modüler monolit) ile yazılmalıdır; kalıcı katman için TypeORM kullanılır. Ekip profili (1 Kotlin / 5–6 Nest.js) ve Faz 1 çıkış hızı bu seçimi destekler.

Ekip profili

1 kişi — Kotlin / Spring

Domain port, entegrasyon modülleri (tahsilat-app, kurye connector), mimari denetim ve code review.

5–6 kişi — JS / Nest.js

Faz 1 çekirdek modüllerin paralel geliştirilmesi: auth, katalog, sepet, sipariş, satıcı paneli API'leri.

Fan Yemek proje atamaları için bkz. Bölüm 9 · Proje Ekibi.

Neden NestJS?

  • Ekip hızı: Faz 1 kapsamı geniş; çoğunluk Nest.js deneyimli — paralel modül geliştirme mümkün.
  • Mimari uyum: NestJS modül/DI yapısı Spring'e benzer; modüler monolit disiplini kolay kurulur.
  • Proje profili: Marketplace backend I/O ağırlıklı (DB, Redis, queue, harici API); Node.js yeterince güçlü.
  • Ekosistem: Redis, BullMQ/Kafka, Elasticsearch, JWT/RBAC, OpenAPI — olgun entegrasyonlar mevcut.
  • ORM: @nestjs/typeorm + TypeORM — entity/repository kalıbı Spring JPA'ya yakın; migration ve PostGIS desteği net.

TypeORM (NestJS ile)

  • Entity tanımı: bu referanstaki modeller (DeliveryShipment, CourierProvider vb.) TypeORM entity olarak domain modüllerine map edilir.
  • Migration: typeorm migration:generate/run — şema değişiklikleri versiyonlu ve CI'da uygulanır.
  • PostGIS: geography(Point) / raw query ile satıcı ve adres mesafe sorguları.
  • Transaction: sipariş + ödeme + outbox yazımları QueryRunner veya @Transactional() ile atomik tutulur.

Kotlin + Spring ne zaman mantıklı olurdu?

Koşul Durum
Şirket standardı zorunlu JVM Belirtilmedi
resto-maxi-app kodu doğrudan paylaşılacak Hayır — ayrı servis kararı
Ekip çoğunlukla JVM deneyimli Hayır — çoğunluk Nest.js
CPU-yoğun batch/analytics ağırlıklı Hayır — event-driven marketplace

resto-maxi-app (Spring Boot + Kotlin) domain ve pattern kaynağı olarak kullanılır; birebir kod paylaşımı şart değildir.

Önerilen stack

Runtime:        Node.js LTS + TypeScript
Framework:      NestJS (modüler monolit)
ORM:            TypeORM (@nestjs/typeorm)
DB:             PostgreSQL + PostGIS
Cache:          Redis (ioredis)
Queue:          BullMQ / RabbitMQ (Kafka — trafik büyüdükçe)
Search:         Elasticsearch
Auth:           Telefon OTP (WhatsApp/MonoChat + Twilio SMS), JWT + refresh rotation, RBAC
API:            REST /v1 (bu referanstaki sözleşme)
Observability:  OpenTelemetry + structured logging
Deploy:         Docker + CI/CD

Faz 1 modül önerisi

auth customer seller catalog cart order payment notification system

Kaçınılması gerekenler

  • Hibrit stack (bazı servisler Nest, bazıları Spring) — Faz 1'de operasyonel yük iki katına çıkar.
  • Kotlin seçip ekibi hızla Kotlin'e çevirmek — 5–6 kişinin ramp-up süresi Faz 1'i geciktirir.
  • Erken microservice — önce modüler monolit; trafik kanıtlandıkça modülleri ayırın.
Seçenek Faz 1 hızı Ekip uyumu resto-maxi reuse
NestJS + TS + TypeORM (önerilen) Yüksek Çok iyi Referans/port
Kotlin + Spring Düşük (darboğaz) Zayıf Doğrudan reuse
Hibrit Orta Kötü Kısmi

8 · Kurye Connector Mimarisi

KuryeMaxi ve benzer sağlayıcılarla entegrasyon adapter pattern ile yürütülür. Fan Yemek'in sorumluluğu ince bir sözleşmedir: teslimat talebi gönder, webhook ile durum al, müşteriye normalize tracking sun.

CourierProvider metodu Yön Açıklama
createDelivery(orderSnapshot) Outbound Sipariş ready → sağlayıcıya teslimat talebi; externalRef + trackingUrl döner
cancelDelivery(externalRef) Outbound Teslimat iptali (sipariş iptalinde)
getTracking(externalRef) Outbound (opsiyonel) Sağlayıcıdan anlık durum/ETA çekme (webhook yedek)
handleWebhook(payload, signature) Inbound Sağlayıcı callback → normalize statü → Order + DeliveryShipment güncelle
Sağlayıcı statüsü (örnek) Fan Yemek normalize
VALIDATED / ASSIGNED (MuditaKurye) assigned
PREPARED / picked up picked_up
ON_DELIVERY / status:1 (KuzeyPOS) on_the_way
DELIVERED / status:2 delivered
FAILED / CANCELLED failed | cancelled
Sağlayıcı Outbound Inbound webhook Public API
KuryeMaxi Partner API (placeholder) Durum callback Yok — partner anlaşması gerekir
MuditaKurye POST /webhook/third-party/order VALIDATED → DELIVERED Dokümantasyon
KuzeyPOS / PaketSefim Sipariş payload push status: 1 (yolda), 2 (teslim) Webhook dok.
Paketiniz Entegre REST + webhook durum kodu + teslimat tarihi Özel dok. (kurumsal)

Yeni sağlayıcı eklemek: (1) CourierProviderAdapter implement et, (2) system'den CourierProvider kaydı + credentials, (3) satıcı/bölge eşlemesi yap, (4) webhook URL'ini sağlayıcıya ver (POST /webhooks/courier/:provider).

9 · Proje Ekibi

Frontend

Yasin Karadeniz

Yasin Karadeniz

Müşteri Mobil Müşteri Web Satıcı Web System Web

Backend

Recep Özen

Recep Özen

Backend
Nuh Yılmaz Şentürk

Nuh Yılmaz Şentürk

Backend
Furkan Öğütçü

Furkan Öğütçü

Backend
Kadir Belen

Kadir Belen

Backend

10 · Billing & Ledger Kararı

Billing revizyonu ertelendi

Aşağıdaki ledger/overdraft yaklaşımı, ayrı bir ürün dokümanında (FanYemek Ödeme Mekanizması — snapshot-tabanlı gelir paylaşımı) revize edilmektedir. Karar netleşene kadar bu bölüm güncel değildir; finans/billing implementasyonu bekletilmiştir. Aktör yeniden adlandırma ve tenant paneli revizyonu bu ertelemeden bağımsız uygulanmıştır.

Billing üç katmandan oluşur: tahsilat-app (kart/APM tahsilat + webhook), ledger (double-entry muhasebe — komisyon split, hakediş birikimi, iade kaydı) ve finans ops (Payout, Refund domain entity'leri). Medici (MongoDB) bu stack'e uymaz; PostgreSQL + NestJS + TypeORM için birincil aday @vynelix/nestjs-accountant değerlendirilecektir.

Overdraft (eksi bakiye) — zorunlu

  • Hesap sahipleri (öncelikle müşteri / User cüzdanı) konfigüre edilmiş limit dahilinde eksi bakiye kullanabilmelidir.
  • Hesap bazlı politika: overdraft hesap başına açılır/kapatılır; varsayılan kapalı, müşteri cüzdanları için açılabilir.
  • Limit: overdraftLimitMinor (TRY kuruş) tavan; aşımda işlem reddedilir.
  • Ledger: overdraft, LIABILITY hesabının negatife inmesine izin verir; debit = credit bütünlüğü korunur.
  • nestjs-accountant Account.allowNegative sunar; limit uygulaması uygulama katmanında (OverdraftGuardService) veya custom ledger entity alanında.
  • Faz 3 MVP dışı: faiz/tahakkuk, otomatik tahsilat döngüsü (altyapı bunları engellememeli).

Birincil — nestjs-accountant POC (Faz 3 öncesi)

  • Paket: @vynelix/nestjs-accountant (README'deki nestjs-accountant adı yanlış)
  • Stack: NestJS 11 + TypeORM + PostgreSQL (Bölüm 7 ile uyumlu)
  • Kapsam: yalnızca ledger çekirdeği; tahsilat-app ve Payout ayrı kalır
  • tenantIdTenant.id
  • referenceType: USER → müşteri cüzdanı (LIABILITY, overdraft açık)
  • referenceType: RESTAURANT → satıcı hakediş hesabı
  • referenceType: ORDER + capture → marketplace split transaction
  • idempotencyKeyPayment.externalRef

Bilinen riskler:

  • createPendingTransactionPOSTED gerçek capture yapmıyor
  • isFrozen enforce edilmiyor
  • v0.0.1, test suite yok, düşük adoption

POC geçerse Faz 3'te billing/ledger modülüne entegre edilir.

Son seçenek — Custom TypeORM Ledger

  • nestjs-accountant go/no-go başarısız olursa devreye girer
  • 4 entity: LedgerAccount, LedgerBalance, LedgerTransaction, LedgerEntry
  • LedgerAccount.allowNegative + overdraftLimitMinor first-class
  • Pessimistic locking + idempotency (Medici / nestjs-accountant pattern referans)
  • Payment, Payout, Refund domain entity'leri korunur
  • Opsiyonel ilham: pgledger (SQL constraint'ler)

POC go/no-go kriterleri (kod POC sonraki adım)

Senaryo Beklenen Kritik
Marketplace split Debit = Credit; satıcı + platform fee + KDV doğru Evet
Idempotency Aynı idempotencyKey duplicate oluşturmaz Evet
Reversal (tam iade) Mirror transaction; bakiye geri alınır Evet
Multi-tenant izolasyon Tenant A bakiyesi Tenant B'den görünmez Evet
Concurrent capture Paralel siparişlerde race condition yok Evet
Auth → capture (pending) Capture'da bakiye güncellenir — bilinen gap; patch veya Kart B Evet
Overdraft — limit içi debit allowNegative: true hesap negatife iner Evet
Overdraft — limit aşımı Limit üstü işlem red; bakiye değişmez Evet
Overdraft — kapalı hesap allowNegative: false → negatif red Evet
Checkout overdraft ile sipariş Eksi cüzdan → split transaction dengeli post Evet
Partial refund Kısmi tutar reversal Orta (Faz 3)
Payout dönemsel toplama Satıcı LIABILITY bakiyesi → Payout Orta

Go: tüm kritik satırlar geçer (overdraft dahil); pending/capture patch kabul edilebilir. No-go: bütünlük, tenant izolasyonu, overdraft limit veya concurrency fail → custom ledger (Kart B).

Veri Modelleri

Domain sekmeleri, alan detayları, ilişkiler ve ER diyagramı

Entity-Relationship Diyagramı

Modeller arası temel ilişkiler — FK alanlarına tıklayarak modele atlayabilirsiniz

erDiagram
    User ||--o{ Address : has
    User ||--o| UserPreferences : has
    User ||--o{ PaymentMethod : has
    User ||--o{ PushToken : has
    User ||--o{ Cart : owns
    User ||--o{ Order : places
    User ||--o{ Review : writes
    User ||--o{ Notification : receives
    SystemUser ||--o{ AuditLog : creates
    SystemUser ||--o{ Refund : processes
    Seller ||--o{ SellerStaff : employs
    Seller ||--o{ WorkingHoursDay : has
    WorkingHoursDay ||--o{ WorkingHoursInterval : contains
    Seller ||--o{ MenuDefinition : owns
    Seller ||--o{ Order : receives
    Seller ||--o{ Payout : receives
    Seller }o--o{ CuisineCategory : tagged
    Tenant ||--o{ TenantUser : has
    Tenant ||--o{ SellerTenant : hosts
    Seller ||--o{ SellerTenant : applies
    Tenant ||--o{ Order : scopes
    Tenant ||--o{ Cart : scopes
    Tenant ||--o{ Payout : scopes
    MenuDefinition ||--o{ MenuDefinitionPublication : published_via
    Tenant ||--o{ MenuDefinitionPublication : receives
    MenuDefinition ||--o{ MenuCategory : contains
    MenuCategory ||--o{ MenuItem : lists
    MenuType ||--o{ MenuItem : classifies
    MenuItem ||--o{ MenuItemOptionGroup : has
    MenuItemOptionGroup ||--o{ MenuItemOption : contains
    Cart ||--o{ CartItem : contains
    Cart }o--|| Seller : from
    CartItem }o--|| MenuItem : references
    Order ||--o{ OrderItem : contains
    Order ||--o| Review : has
    Order ||--o| Payment : paid_by
    Order ||--o| DeliveryShipment : fulfilled_by
    Order }o--|| Address : delivered_to
    DeliveryShipment }o--|| CourierProvider : via
    CourierProvider ||--o{ DeliveryShipment : handles
    DeliveryShipment ||--o{ DeliveryStatusEvent : logs
    Seller ||--o| SellerCourierConfig : courier_config
    SellerCourierConfig }o--|| CourierProvider : uses
    Payment ||--o{ Refund : may_have
    Coupon }o--o{ Cart : applied_to
    User ||--o{ Favorite : saves
    Favorite }o--o| Seller : refs
    Favorite }o--o| MenuItem : refs
    User ||--o{ UserCoupon : wallet
    UserCoupon }o--|| Coupon : of
    Campaign }o--o| Coupon : promotes
    User ||--o{ SupportTicket : opens
    SupportTicket }o--o| Order : about
    Tenant ||--|| TenantPaymentConfig : payment_config
    Tenant ||--o{ Payment : scopes
    FulfillmentType ||--o{ SellerFulfillmentSetting : offered
    PaymentMethodType ||--o{ SellerPaymentSetting : accepted
    MealCardProvider ||--o{ SellerPaymentSetting : meal_card
    Seller ||--o{ SellerFulfillmentSetting : configures
    Seller ||--o{ SellerPaymentSetting : accepts
    SellerFulfillmentSetting }o--o| Tenant : tenant_override
    SellerPaymentSetting }o--o| Tenant : tenant_override
    Order }o--|| FulfillmentType : fulfillment
    Order }o--|| PaymentMethodType : payment_type
    Province ||--o{ District : contains
    District ||--o{ Neighborhood : contains
    Province ||--o{ Neighborhood : denormalized
    Neighborhood ||--o{ NeighborhoodCell : divided_into
    Seller ||--o{ DeliveryZone : serves
    DeliveryZone ||--o{ DeliveryZoneCell : covers
    DeliveryZone }o--o| Tenant : tenant_override
    DeliveryZoneCell }o--o| Neighborhood : sourced_from

Çalışma Zamanı Akışları & Davranış

Sistemin "nasıl çalıştığı": sipariş yaşam döngüsü, ödeme, kurye dağıtımı ve realtime/event mimarisi

1 · Sipariş Statü Makinesi

Siparişin tüm yaşam döngüsü tek bir durum makinesi ile yönetilir. Geçişler yalnızca tanımlı yönlerde ve yetkili aktör tarafından yapılabilir; geçersiz geçiş 409 INVALID_STATUS_TRANSITION döndürür.

Geçiş Tetikleyen Ön koşul / Not
pending → confirmed seller (owner · temsilci) Online ödemede authorize edilmiş olmalı; kabul ile capture. COD/banka havalesinde doğrudan onay.
pending → rejected seller · system Satıcı reddi veya zaman aşımı; authorize iptali (void)
confirmed → preparing seller Hazırlık başladı
preparing → ready seller Paket hazır; pickup dışında dispatch tetiklenir
ready → delivered seller · customer Yalnızca pickup — müşteri aldı / satıcı teslim etti; kurye yok
ready → picked_up connector · webhook delivery_to_address veya tribune_delivery; dispatch sonrası webhook
picked_up → on_the_way connector · webhook Sağlayıcı ON_DELIVERY / status:1; trackingUrl müşteriye açılır
on_the_way → delivered connector · webhook Sağlayıcı DELIVERED / status:2; payout'a sayılır
* → cancelled customer · system Yalnızca pending/confirmed aşamasında müşteri; sonrası system + iade

Terminal durumlar: delivered, cancelled, rejected. Her geçiş order.status_changed eventi yayar.

2 · Ödeme Akışı (tahsilat-app)

Online ödemeler (card_online, meal_card_online) tenant'ın TenantPaymentConfig.tahsilatMerchantRef profili üzerinden authorize → capture modeliyle yürür. Kapıda nakit/yemek kartı ve banka havalesi tahsilat-app authorize akışına girmez.

Ödeme türüne göre dallanma

  • card_online / meal_card_online: authorize (checkout) → capture (satıcı kabul) → komisyon SellerTenant.commissionRate ile tenant kapsamında hesaplanır; capture sonrası split transaction ledger'a yazılır (Bölüm 10).
  • cash_on_delivery / meal_card_on_delivery: sipariş isPrepaid: false; teslimatta/satıcıda tahsil; komisyon yine tenant komisyon oranından.
  • bank_transfer: tenant IBAN (TenantPaymentConfig.bankTransferIban) gösterilir; sipariş pending_payment — system/satıcı manuel onay (MVP).
  • meal_card_online: tahsilat-app APM (Pluxee/Multinet vb.); tenant config'de aktif provider + satıcı üye iş yeri metadata gerekir.

Kritik kurallar

  • Tenant scope: tüm tahsilat-app çağrıları aktif tenant'ın merchant ref'i ile yapılır; cross-tenant yönlendirme yasak.
  • Idempotency: her ödeme isteği sipariş bazlı Idempotency-Key ile gönderilir; tekrar denemede çift çekim olmaz (Payment.externalRef).
  • Başarısız authorize: sipariş 402 ile reddedilir, sipariş kaydı oluşmaz veya payment_failed işaretlenir.
  • Capture edilmiş ama reddedildi/iptal: sistem otomatik Refund başlatır; tahsilat-app webhook'u ile refunded teyit edilir.
  • PCI sınırı: kart verisi yalnızca tahsilat-app'te; Fan Yemek sadece token + işlem referansı tutar.
  • Komisyon: SellerTenant.commissionRateTenant.defaultCommissionRateSystemConfig.platformCommissionDefault.

3 · Kurye Dispatch (Connector)

Sipariş ready olunca teslimat CourierConnector üzerinden dış sağlayıcıya iletilir. Satıcı konfigürasyonundaki CourierProvider seçilir; durum güncellemeleri inbound webhook ile alınır.

Akış notları

  • Otomatik dispatch: yalnızca delivery_to_address ve tribune_delivery — sipariş ready olunca connector createDelivery(orderSnapshot) çağırır (pickup hariç).
  • Manuel tetik: satıcı panelden POST /orders/:id/dispatch ile yeniden gönderim veya gecikmeli dispatch.
  • Inbound webhook: sağlayıcı POST /webhooks/courier/:provider ile durum bildirir; HMAC doğrulanır, statü normalize edilir.
  • Müşteri tracking: trackingUrl varsa proxy/passthrough; yoksa normalize statü + ETA.
  • Geri dönüş: delivery.failed veya timeout'ta satıcı/operasyona eskalasyon.

4 · Realtime & Event Mimarisi

Domain olayları message broker (Kafka/RabbitMQ) üzerinden yayılır; istemcilere anlık yansıma WebSocket/SSE ile yapılır. Yazma ile event yayımı outbox pattern ile atomik tutulur, consumer'lar idempotenttir.

Event Yayınlayan Tüketen / Kanal Payload özeti
order.created Sipariş servisi Satıcı (SSE), bildirim, arama orderId, sellerId, items, total
order.status_changed Sipariş servisi Müşteri (WS), satıcı, bildirim orderId, from, to, at
payment.authorized / captured / refunded Ödeme servisi Sipariş, finans, bildirim orderId, paymentId, amount, status
delivery.requested Connector servisi Satıcı, audit orderId, providerId, externalRef
delivery.status_changed Connector (webhook handler) Müşteri (WS), satıcı, bildirim orderId, from, to, trackingUrl
delivery.failed Connector servisi Satıcı, operasyon, bildirim orderId, providerId, reason
notification.broadcast System Push/SMS/mail kuyruğu target, title, body

5 · Teslimat & Ödeme Seçenekleri (Checkout)

Satıcılar system yönetimli katalogdan (FulfillmentType, PaymentMethodType) kendine uygun seçenekleri işaretler. Checkout'ta görünen liste: TenantPaymentConfig ∩ SellerPaymentSetting (tenant override varsa öncelikli).

Sektör referansı (kısa)

  • Trendyol Yemek: online kart + Pluxee/Multinet/Edenred/Setcard (üye satıcılarda); kapıda nakit/yemek kartı geçerli satıcılarda.
  • Getir Yemek: platform kuryesinde dijital ödeme; kapıda ödeme yalnızca Satıcı Getirsin modelinde.
  • Yemeksepeti: Gel Al + adrese teslim; ödeme checkout'ta teslimat türüne göre filtrelenir.
  • Fan Yemek farkı: stadyum senaryosunda tribune_delivery (engelli tribününe teslim, tribün no).
FulfillmentType İzin verilen ödemeler Kurye dispatch Checkout alanları
delivery_to_address card_online, meal_card_online, cash_on_delivery*, meal_card_on_delivery*, bank_transfer Evet addressId zorunlu
pickup card_online, meal_card_online, cash_on_delivery, bank_transfer Hayır Adres yok
tribune_delivery card_online, meal_card_online, cash_on_delivery*, meal_card_on_delivery* Evet (tribün noktası) tribuneNumber, block, accessNote

* Kapıda ödemeler: satıcı requiresOwnCourier=true veya kurye sağlayıcı COD destekliyorsa. Tenant'ta kapalı ödeme türü satıcı seçse bile checkout'ta görünmez.

6 · Çalışma Saatleri (Haftalık Program)

Satıcı genel ayar olarak haftalık program tutar (WorkingHoursDay + WorkingHoursInterval). Günde birden fazla dilim desteklenir (örn. 08:00–12:00, 13:00–22:00). Gün: ISO 1=Pazartesi … 7=Pazar.

  • Program vs. isOpen: program haftalık şablon; isOpen=false manuel kapama (tatil, acil).
  • Sipariş kabulü: isOpen === true VE openMinutes ≤ nowMinutes ≤ closeMinutes (TZ: Europe/Istanbul).
  • Gel Al / tribün teslimat: aynı haftalık program geçerli; fulfillment tipine göre ek kısıt yok (MVP).
  • İleri tarihli sipariş (scheduledAt): hedef zaman ilgili günün interval'larından birine düşmeli.
  • Sayısal alanlar: openMinutes/closeMinutes sunucuda HH:mm'den türetilir; interval'da denormalize day/sellerId arama için.
  • PUT: atomik replace — tüm hafta bir seferde; max 4 interval/gün.
Kural 422 koşulu
Gün numarası day ∈ 1..7, schedule'da tekrar yok
Kapalı gün isClosed:true → intervals[] boş
Açık gün isClosed:false → en az 1 interval
Dilim sırası openTime < closeTime; aynı günde çakışma yok
Sayısal alan PUT'ta openMinutes/closeMinutes yok sayılır

7 · Hizmet Bölgesi & Min. Sepet (H3)

Marketplace teslimatı iki katmanla yönetilir: referans coğrafya (system seed: İl → İlçe → Mahalle; hexagon havuzu yalnız mahallede) ve satıcı teslimat bölgesi (DeliveryZone + DeliveryZoneCell). Runtime lookup saf H3: müşteri lat/lng → hücre → eşleşen bölge → minOrderAmount / deliveryFee / ETA.

Sektör referansı

  • Getir Yemek: çoklu bölge, bölge bazlı min sepet + teslimat süresi, haritada H3 altıgen grid.
  • Trendyol Yemek: "Alan Seç", "Mahalle Doldur", "Mahalle Ayarı" — bölge başına farklı sepet tutarı (ör. 300–2000 TL).
  • Fan Yemek: Seller.minOrder / SystemConfig.minOrderAmount yalnız fallback; asıl min sepet DeliveryZone.minOrderAmount.
Tablo boundary H3 hücre
Province Normalde null; opsiyonel
District Normalde null; opsiyonel
Neighborhood Normalde zorunlu (GeoJSON) NeighborhoodCell (persist)
DeliveryZone DeliveryZoneCell ⊆ NeighborhoodCell
  • H3 res 9 (~0.10 km²/hücre) — SystemConfig.deliveryZoneH3Resolution tüm sistemde sabit.
  • Persist: H3 deterministik; mahalle kaydında bir kez polygonToCellsNeighborhoodCell (panel her açılışında yeniden hesaplanmaz).
  • Subset garantisi: satıcı seçimi ∩ mahalle havuzu; sınır dışı hücre → 422.
  • Yarıçap: maxServiceRadiusKmDefault (~15) + Seller.maxServiceRadiusKm; dışı backend'de gönderilmez (hardcut).
  • SQL: FK zinciri + normalize hücre tablosu + h3_index btree index; il/ilçe ON DELETE RESTRICT.

Örnek veri

{
  "province": { "id": "il_55", "name": "Samsun", "code": "55", "boundary": null },
  "district": { "id": "ilce_5502", "provinceId": "il_55", "name": "Atakum", "boundary": null },
  "neighborhood": {
    "id": "mah_mevlana", "districtId": "ilce_5502", "provinceId": "il_55",
    "name": "Mevlana", "pathString": "/il_55/ilce_5502/mah_mevlana/",
    "h3Resolution": 9, "cellCount": 98
  },
  "neighborhoodCell": { "neighborhoodId": "mah_mevlana", "h3Index": "891ec1a4d4bffff" },
  "deliveryZone": { "id": "z1", "sellerId": "r_123", "name": "Bölge 1", "minOrderAmount": 350, "etaMinMinutes": 15, "etaMaxMinutes": 25 },
  "deliveryZoneCell": { "zoneId": "z1", "h3Index": "891ec1a4d4bffff", "neighborhoodId": "mah_mevlana" }
}

Sorgu örnekleri

-- 1) Nokta → hücre → satıcıın uygulanan bölgesi (lookup, saf H3)
-- h3 = latLngToCell(lat, lng, 9)
SELECT z.* FROM delivery_zone_cell c
JOIN delivery_zone z ON z.id = c.zone_id
WHERE c.h3_index = :h3 AND z.seller_id = :sellerId AND z.is_active
ORDER BY z.priority LIMIT 1;

-- 2) "Mahalle Doldur"
INSERT INTO delivery_zone_cell (zone_id, h3_index, neighborhood_id)
SELECT :zoneId, nc.h3_index, nc.neighborhood_id
FROM neighborhood_cell nc WHERE nc.neighborhood_id = :neighborhoodId;

-- 2b) "İlçe Doldur" (FK join)
INSERT INTO delivery_zone_cell (zone_id, h3_index, neighborhood_id)
SELECT :zoneId, nc.h3_index, nc.neighborhood_id
FROM neighborhood_cell nc
JOIN neighborhood n ON n.id = nc.neighborhood_id
WHERE n.district_id = :districtId AND n.is_active;

-- 3) Yarıçap-sınırlı mahalleler (hardcut) + X/Y
-- haversine(centroid) <= maxServiceRadiusKm

Tasarım Kapsam Kontrolü (Figma)

SAMYEMEK müşteri tasarımı ile API ağacının karşılaştırması

Tasarımdan eklenen eksikler

SAMYEMEK müşteri tasarımındaki ekranlar API ağacıyla karşılaştırıldı. Çoğu ekran (hesap, adres, ödeme yöntemleri, sepet, checkout, sipariş, arama, satıcı detay, bildirim) zaten karşılanıyordu. Tasarımda olup eksik olan ve Customer API'ye eklenen özellikler:

  • Favoriler — satıcı + ürün favorileri (/favorites); "Favorilerim" ekranı.
  • İndirim Kuponlarım — müşteri kupon cüzdanı + kod ile ekleme (/coupons, /coupons/claim).
  • Kampanyalar — bottom-nav ana sekmesi (/campaigns).
  • Yardım & Destek — iletişim, SSS, destek talebi (/support/*); "Yardım Merkezi" ekranı.
  • Tekrar sipariş ver — favoriler/geçmişten reorder (/orders/:id/reorder).

Yeni modeller: Favorite, UserCoupon, Campaign, SupportTicket — Veri Modelleri ve ER diyagramına işlendi.

Kapsam dışı: Samsunspor modülü

Tasarımda bottom-nav'da merkezi bir "Samsunspor" sekmesi ve Puan Tablosu ekranı var (lig sıralaması, fikstür, takım durumu). Bu, marketplace çekirdeğinin dışında bir içerik modülüdür ve büyük olasılıkla dış spor-veri sağlayıcısı entegrasyonu veya bir CMS gerektirir. Şimdilik kapsam dışı kabul edildi; ileride ayrı bir modül (ör. /v1/content/*) olarak ele alınması önerilir.

API Referansı

Aktör seçip endpoint detaylarını (parametre, rol, yanıt) inceleyin