GİB Mükellef Sorgulama Servisi
Türkiye'de e-Fatura ve e-İrsaliye sistemlerinde mükellef sorgulama ihtiyacı hemen her uygulamada tekrar eden bir problemdir. GİB'in yayınladığı PK/GB XML listeleri milyonlarca kaydı barındırır ve bu verileri her uygulamanın kendi bünyesinde yönetmesi ciddi bir teknik yük oluşturur.
gib-user-list bu sorunu bir kez çözüp tüm iç uygulamalarınıza sunma felsefesiyle tasarlanmıştır.
GitHub'da Açık Kaynak
gib-user-list — .NET 9, PostgreSQL, EF Core 9 üzerine inşa edilmiş, MIT lisanslı açık kaynak proje. github.com/mersel-os/gib-user-list
Kurulum, yapılandırma veya production ortamına taşıma konusunda desteğe mi ihtiyacınız var? Ekibimizle iletişime geçin — projenizi birlikte hayata geçirelim. Tüm açık kaynak projelerimiz için açık kaynak sayfamızı ziyaret edin.
Temel Avantajlar
| Avantaj | Detay |
|---|---|
| ~200 MB RAM | Stream tabanlı XML parsing ile milyonlarca kaydı düşük bellekle işler |
| Sıfır Kesinti | PostgreSQL Materialized View ile güncelleme sırasında API kesintiye uğramaz |
| DB Bağımsız | Uygulamalarınız MSSQL, MongoDB, Oracle kullanıyor olabilir — fark etmez |
| Ultra Hızlı Sorgulama | VKN/TCKN sorguları in-memory cache'ten sub-millisaniye |
| Her Platformda Çalışır | Kubernetes, Linux systemd, Docker Compose veya Windows Servisi |
| Hazır .NET SDK | NuGet paketi ile tek satırda entegre, HMAC otomatik |
Tipik Kullanım Senaryosu
Bu servis, doğrudan dış dünyaya açılmak yerine internal mikroservis olarak tasarlanmıştır:
Kendi uygulamanızda mükellef tablosu tutmanıza, GİB XML'lerini parse etmenize gerek kalmaz. Backend servisiniz bu internal mikroservise bir HTTP çağrısı yapar, sonucu alır ve kendi iş mantığında kullanır.
Mikroservis mimarisinde API katmanını dilediğiniz kadar yatay ölçeklendirebilirsiniz. Her instance aynı Materialized View'dan okuduğu için performans sabit kalır.
Mimari
Proje Clean Architecture prensiplerine uygun olarak katmanlı bir yapıdadır:
| Bileşen | Teknoloji |
|---|---|
| Runtime | .NET 9 |
| Veritabanı | PostgreSQL 17 |
| ORM | EF Core 9 |
| API | Minimal API |
| Metrikler | OpenTelemetry + Prometheus |
| Test | xUnit + Testcontainers |
| Container | Docker |
| Orkestrasyon | Kubernetes (CronJob + Deployment) |
API ve Worker ayrı proseslerdir. API sürekli çalışır ve sorguları yanıtlar; Worker periyodik olarak tetiklenir, GİB verilerini günceller ve kapanır. Bu ayrım sayesinde güncelleme sırasında API performansı etkilenmez ve Worker'ın kullandığı bellek işi bitince tamamen serbest kalır.
Hızlı Başlangıç
Docker Compose ile
cd docker
docker compose up -d
| Servis | Adres |
|---|---|
| PostgreSQL 17 | localhost:5432 |
| API | http://localhost:8080 |
| Scalar API Dokümantasyonu | http://localhost:8080/scalar/v1 |
| Sağlık Kontrolü | http://localhost:8080/health |
| Worker | Çalışır, verileri senkronize eder, çıkar |
Monitoring Stack ile
cd monitoring
export POSTGRES_PASSWORD=gizli-sifre
export GRAFANA_ADMIN_PASSWORD=grafana-sifre
docker compose up -d
| Servis | Adres |
|---|---|
| API | http://localhost:8080 |
| Prometheus | http://localhost:9090 |
| Grafana | http://localhost:3000 |
| Metrikler | http://localhost:8080/metrics |
Senkronizasyon Mekanizması
Worker'ın senkronizasyon akışı 12 adımdan oluşur:
- GİB'den PK + GB ZIP dosyalarını paralel indir (retry destekli)
- ZIP'ten XML dosyalarını çıkar
- XmlReader ile akış tabanlı parse — tüm listeyi belleğe yüklemez
- PostgreSQL COPY protokolü ile geçici tablolara toplu yaz (25K batch)
- Advisory Lock — eş zamanlı sync çalışmasını engeller
- Diff Engine —
content_hashile değişiklik tespiti (added/modified/removed) - Safe removal guard — toplam silme yüzdesi eşik aşarsa silme atlanır
- REFRESH MATERIALIZED VIEW CONCURRENTLY (retry destekli, maks 3 deneme)
- Belge türüne göre XML.GZ arşiv üret (PK+GB birleşik)
- Eski arşiv dosyalarını retention'a göre temizle
sync_metadatagüncelle- Webhook bildirimi gönder, çık — ~200 MB RAM tamamen serbest kalır
Neden Stream Tabanlı?
GİB'in mükellef XML listeleri milyonlarca kayıt içerir. Tüm dosyayı belleğe yükleyip XmlDocument ile deserialize etmek gigabyte'larca RAM tüketir. Bu servis XmlReader ile tek kayıt tek kayıt okur ve her 25.000 kayıtta PostgreSQL'e COPY ile yazar. Toplam bellek tüketimi ~200 MB civarında kalır.
Materialized View ile Sıfır Kesinti
REFRESH MATERIALIZED VIEW CONCURRENTLY PostgreSQL'e özgü güçlü bir yetenektir: eski veri okunmaya devam ederken arka planda yeni veri hazırlanır, tamamlandığında atomik olarak değiştirilir.
| Tablo / Nesne | Tür | Açıklama |
|---|---|---|
e_invoice_gib_users | Ana tablo | e-Fatura mükellefleri (content_hash ile diff) |
e_despatch_gib_users | Ana tablo | e-İrsaliye mükellefleri |
gib_user_changelog | Changelog | Değişiklik kayıtları: added, modified, removed |
mv_e_invoice_gib_users | Materialized View | API buradan okur — sıfır kesinti |
mv_e_despatch_gib_users | Materialized View | API buradan okur |
sync_metadata | Metadata | Son sync zamanı, durum, hata özeti, sayılar |
MV yenileme başarısız olursa 5 saniye aralıklarla 2 kez daha denenir; tüm denemeler başarısız olursa kritik webhook bildirimi gönderilir. MSSQL Server'da bu davranışın doğrudan karşılığı yoktur.
API Endpoint'leri
Sorgulama
| Metod | Yol | Açıklama |
|---|---|---|
GET | /api/v1/einvoice/{identifier} | VKN/TCKN ile e-Fatura mükellefi sorgula |
GET | /api/v1/einvoice?search=...&page=1&pageSize=20 | e-Fatura mükellefi ara |
POST | /api/v1/einvoice/batch | Toplu VKN/TCKN sorgula (maks 100) |
GET | /api/v1/edespatch/{identifier} | VKN/TCKN ile e-İrsaliye mükellefi sorgula |
GET | /api/v1/edespatch?search=... | e-İrsaliye mükellefi ara |
POST | /api/v1/edespatch/batch | Toplu e-İrsaliye sorgula (maks 100) |
Değişiklik Takibi (Delta Sync)
| Metod | Yol | Açıklama |
|---|---|---|
GET | /api/v1/einvoice/changes?since=...&until=... | e-Fatura değişiklikleri |
GET | /api/v1/edespatch/changes?since=...&until=... | e-İrsaliye değişiklikleri |
Her kayıt changeType alanı içerir: added, modified veya removed. Retention süresi dışındaki istekler 410 Gone döner — bu durumda tüketici tam listeyi yeniden indirmelidir.
Arşiv (Tam Liste)
| Metod | Yol | Açıklama |
|---|---|---|
GET | /api/v1/einvoice/archives/latest | En son e-Fatura tam listesi (XML.GZ) |
GET | /api/v1/edespatch/archives/latest | En son e-İrsaliye tam listesi (XML.GZ) |
Durum ve Sağlık
| Metod | Yol | Açıklama |
|---|---|---|
GET | /api/v1/status | Senkronizasyon durumu, hata özeti, mükellef sayıları |
GET | /health | Sağlık kontrolü (PostgreSQL, sync freshness, archive storage) |
GET | /metrics | Prometheus metrikleri |
Kullanım Örnekleri
# VKN ile e-Fatura mükellefi sorgula
curl http://localhost:8080/api/v1/einvoice/1234567890
# Ünvana göre arama
curl "http://localhost:8080/api/v1/einvoice?search=MERSEL&page=1&pageSize=20"
# Delta sync — son 1 saatteki değişiklikler
curl "http://localhost:8080/api/v1/einvoice/changes?since=2026-02-27T00:00:00Z"
# Senkronizasyon durumunu kontrol et
curl http://localhost:8080/api/v1/status
Tüm sorgu ve arşiv endpoint'leri yanıtta X-Last-Sync-At başlığını döner:
X-Last-Sync-At: 2026-02-19T03:05:12.0000000+03:00
Tüketici Senkronizasyon Protokolü
Tüketicilerin mükellef listesini verimli şekilde güncel tutması için bir protokol sunulur:
- Bootstrap:
/api/v1/einvoice/archives/latestile tam listeyi XML.GZ olarak indir - Delta Takibi: Periyodik olarak
/changes?since=...ile değişiklikleri sorgula - Re-bootstrap: 410 Gone dönerse retention süresi dışına düşülmüş demektir — tam listeyi yeniden indir
HMAC-SHA256 Kimlik Doğrulama
İki mod desteklenir:
Devre dışı (varsayılan): Tüm istekler anonim geçer.
Authentication__Enabled=false
HMAC aktif: Her istemci AccessKey + SecretKey çifti ile tanımlanır.
Authentication__Enabled=true
Authentication__TimestampToleranceSeconds=300
Authentication__Clients__0__AccessKey=belgehub-prod
Authentication__Clients__0__SecretKey=gizli-anahtar-buraya
Authentication__Clients__0__Name=BelgeHub
| Başlık | Açıklama |
|---|---|
X-Access-Key | İstemcinin public erişim anahtarı |
X-Timestamp | Unix epoch (saniye) |
X-Signature | HMAC-SHA256(SecretKey, "{METHOD}\n{PathAndQuery}\n{Timestamp}") |
Sunucu tarafında timestamp toleransı (varsayılan 5 dk) ve sabit-zaman imza karşılaştırması uygulanır.
Webhook Bildirimleri
Kritik olaylar için Slack ve/veya HTTP webhook desteği:
Webhooks__Enabled=true
Webhooks__Slack__Enabled=true
Webhooks__Slack__WebhookUrl=https://hooks.slack.com/services/T.../B.../xxx
Webhooks__Slack__Channel=#gib-sync-alerts
| Olay | Önem | Açıklama |
|---|---|---|
SyncCompleted | Bilgi | Senkronizasyon başarıyla tamamlandı |
SyncFailed | Kritik | Senkronizasyon başarısız oldu |
SyncPartial | Uyarı | Advisory lock çakışması veya arşiv uyarıları |
RemovalGuardTriggered | Uyarı | Güvenli silme koruyucusu toplu silmeyi engelledi |
MvRefreshFailed | Kritik | Materialized view yenileme başarısız |
HealthStatusChanged | Değişken | Sağlık kontrolü durumu değişti |
HTTP webhook'ta Secret tanımlıysa gövdenin HMAC-SHA256 imzası X-Webhook-Signature: sha256=... başlığında gönderilir. Her kanal hangi olayları alacağını NotifyOn ile bağımsız olarak seçebilir.
Webhook gönderim hataları ana iş akışını asla engellemez — yalnızca loglanır. Slack bildirimleri önem derecesine göre renklendirilmiş Block Kit mesajları olarak gönderilir.
Güvenli Silme Koruyucusu
Senkronizasyon sırasında silinecek mükellef oranı MaxAllowedRemovalPercent eşiğini aşarsa (örn. GİB'den hatalı/boş liste gelmesi durumunda) silme işlemi otomatik olarak atlanır ve webhook bildirimi gönderilir.
GibEndpoints__MaxAllowedRemovalPercent=10 # Güvenli silme eşiği (%)
Bu, veri kaybını önleyen kritik bir koruma mekanizmasıdır. GİB'den hatalı veya boş bir liste gelmesi durumunda milyonlarca mükellef kaydının silinmesi engellenir.
Önbellekleme
Caching__Provider=Memory # "Memory" (varsayılan) veya "Redis"
Caching__DefaultTtlMinutes=60
Caching__RedisConnectionString=localhost:6379
Yalnızca VKN/TCKN ile yapılan doğrudan sorgular (/api/v1/einvoice/{identifier}) önbelleğe alınır. Arama sorguları her zaman veritabanından gelir.
Arşiv Depolama
İki depolama seçeneği:
| Provider | Yapılandırma |
|---|---|
| FileSystem (varsayılan) | ArchiveStorage__Provider=FileSystem + BasePath=/data/gib-archives |
| S3 / MinIO | ArchiveStorage__Provider=S3 + BucketName, Region, ServiceUrl |
.NET İstemci SDK
Kurulum
dotnet add package MERSEL.Services.GibUserList.Client
DI Kaydı
// Auth devre dışı
builder.Services.AddGibUserListClient(options =>
{
options.BaseUrl = "https://gib-userlist.example.com";
});
// HMAC kimlik doğrulamalı
builder.Services.AddGibUserListClient(options =>
{
options.BaseUrl = "https://gib-userlist.example.com";
options.AccessKey = "belgehub-prod";
options.SecretKey = "gizli-anahtar-buraya";
});
AccessKey ve SecretKey verildiğinde tüm çıkan istekler otomatik olarak HMAC-SHA256 ile imzalanır.
Kullanım
public class MyService(IGibUserListClient gibuserClient)
{
public async Task Example(CancellationToken ct)
{
// VKN/TCKN ile sorgula
var user = await gibuserClient
.GetEInvoiceGibUserAsync("1234567890", ct: ct);
// Ünvana göre ara
var results = await gibuserClient
.SearchEInvoiceGibUsersAsync("MERSEL", ct: ct);
// Toplu sorgulama (maks 100)
var batch = await gibuserClient
.BatchGetEInvoiceGibUsersAsync(
["1234567890", "9876543210"], ct);
// e-İrsaliye mükellefi sorgula
var despatch = await gibuserClient
.GetEDespatchGibUserAsync("1234567890", ct: ct);
// Delta sync
var changes = await gibuserClient
.GetEInvoiceChangesAsync(
since: DateTime.Now.AddHours(-1), ct: ct);
// Tam mükellef listesi arşivi (bootstrap)
var archive = await gibuserClient
.GetLatestEInvoiceArchiveAsync(ct);
// Senkronizasyon durumu
var status = await gibuserClient
.GetSyncStatusAsync(ct);
}
}
Tüketici Sync Örneği
public async Task SyncLocalDatabase(
IGibUserListClient client, CancellationToken ct)
{
try
{
var changes = await client.GetEInvoiceChangesAsync(
since: _lastSyncTime, ct: ct);
foreach (var change in changes.Data.Changes)
{
switch (change.ChangeType)
{
case "added":
case "modified":
await UpsertLocalUser(change);
break;
case "removed":
await DeleteLocalUser(change.Identifier);
break;
}
}
_lastSyncTime = changes.LastSyncAt ?? DateTime.Now;
}
catch (GibUserListSyncExpiredException)
{
// 410 Gone — tam listeyi tekrar indir
var archive = await client
.GetLatestEInvoiceArchiveAsync(ct);
if (archive.Data is not null)
await RebuildLocalDatabase(archive.Data);
}
}
Dağıtım
Kubernetes
kubectl apply -f k8s/api-deployment.yaml
kubectl apply -f k8s/updater-cronjob.yaml
- API: Deployment + HPA ile yatay ölçeklendirme, sürekli çalışır
- Worker: CronJob, günde 4 kez çalışır (03:00, 09:00, 15:00, 21:00), bitince kapanır.
concurrencyPolicy: Forbidile aynı anda birden fazla çalışması engellenir.
Linux (systemd + cron)
# API: systemd servisi
sudo systemctl enable --now gibuserlist-api
# Worker: crontab ile periyodik tetikleme
echo "0 3 * * * /opt/gib-userlist/Worker.Updater" | sudo crontab -
Docker Compose
cd docker
docker compose up -d
PostgreSQL, API ve Worker tek komutla ayağa kalkar.
Observability
Prometheus Metrikleri (/metrics)
| Metrik | Tip | Açıklama |
|---|---|---|
gibuserlist_queries_total | Counter | Toplam sorgu sayısı |
gibuserlist_queries_duration_milliseconds | Histogram | Sorgu süresi (ms) |
gibuserlist_sync_total | Counter | Senkronizasyon sayısı |
gibuserlist_sync_duration_seconds | Histogram | Senkronizasyon süresi |
gibuserlist_sync_users_processed_total | Counter | İşlenen kullanıcı sayısı |
gibuserlist_cache_hits_total | Counter | Önbellek isabet sayısı |
gibuserlist_cache_misses_total | Counter | Önbellek ıskalama sayısı |
gibuserlist_auth_requests_total | Counter | Auth isteği sayısı |
gibuserlist_sync_changes_total | Counter | Tespit edilen değişiklik sayısı |
gibuserlist_sync_removal_skipped_total | Counter | Güvenli silme koruyucusu tetiklenme |
gibuserlist_mv_refresh_duration_seconds | Histogram | MV yenileme süresi |
gibuserlist_users_einvoice_count | Gauge | Aktif e-Fatura mükellef sayısı |
gibuserlist_users_edespatch_count | Gauge | Aktif e-İrsaliye mükellef sayısı |
Grafana Dashboard
monitoring/ klasöründe hazır dashboard, 7 bölüm, 25+ panel:
- Genel Bakış — Toplam sorgu, hata oranı, ortalama süre, cache hit oranı
- Sorgu Performansı — Rate, p50/p95/p99, belge türü dağılımı
- Sync Job — İşlem sayısı, süre, kullanıcı sayısı
- Cache — Hit/miss rate, hit oranı
- Kimlik Doğrulama — İstemci bazında sorgu rate, başarısızlık nedenleri
- HTTP İstekleri — Status code dağılımı, response time
- .NET Runtime — GC heap, thread pool, bellek
Sonuç
gib-user-list, e-Dönüşüm uygulamalarının en yaygın ihtiyaçlarından biri olan mükellef sorgulama problemini bir kez çözüp tüm altyapınıza sunar. Stream tabanlı parsing ile düşük bellek kullanımı, Materialized View ile sıfır kesinti güncelleme, delta sync ile verimli tüketici protokolü ve kapsamlı observability ile production-grade bir çözümdür.
Kendi uygulamanızda mükellef tablosu tutmanıza, GİB XML'lerini parse etmenize, güncelleme job'ları yazmanıza gerek kalmaz.
Kurulum ve Entegrasyon Desteği
GİB mükellef sorgulama servisini kurmak, Kubernetes'e deploy etmek veya mevcut e-Dönüşüm altyapınıza entegre etmek için uzman ekibimizden destek alın.
Bize Ulaşın- GitHub: github.com/mersel-os/gib-user-list
- Detay Sayfası: Proje Detayları
- NuGet: MERSEL.Services.GibUserList.Client
- Lisans: MIT