Referans Mimari: Mikroservis veri katmanı
Mikroservislerde en kritik karar veri sahipliğidir: her servis kendi verisine sahip olmalı (database-per-service) ve başka servisin tablosuna doğrudan girmemeli. Bu ağaç; paylaşılan DB anti-desenini, servisler arası tutarlılığı dağıtık transaction olmadan sağlamayı (saga + outbox), veriyi CDC ile senkronlamayı ve servisler arası sorguyu (API composition vs CQRS okuma modeli) adım adım ele alır. Amaç; servisleri gerçekten bağımsız ve ayrı deploy edilebilir tutmaktır.
Servisler veriye nasıl sahip: ortak DB mi, her servise ayrı mı?
Mikroservis mimarisinin bel kemiği veri izolasyonudur. Servisler tek bir paylaşılan veritabanını mı kullanıyor (gizli bağımlılık), her servisin kendi veritabanı mı var (doğru desen), yoksa asıl zorluk servisler arası tutarlılık/sorgu mu? Önce mevcut durumu belirle.
- Tüm servisler tek paylaşılan DB'yi kullanıyor
- Her servisin DB'si ayrı; ama işlemler servisler arası tutarlılık istiyor
- Her servisin DB'si ayrı; ama birleşik sorgu/rapor gerekiyor
Servisler arası bir iş akışını nasıl tutarlı tutacaksın?
Birden çok servisi kapsayan bir işlemde (sipariş oluştur → ödeme al → stok düş) klasik dağıtık transaction (2PC) kırılgan ve ölçeklenmez. Bunun yerine akışı yerel transaction + olay adımlarına bölmek (saga) ve olayları güvenle yayınlamak (outbox) gerekir. Hangi problemle uğraşıyorsun?
- Çok servisli iş akışını atomik tutmaya çalışıyorum (2PC?)
- Yerel yazma ile olay yayınını tutarlı yapamıyorum (ikili yazma)
Birden çok servisin verisini gösteren sorguyu nasıl karşılayacaksın?
Veri servislere bölününce tek bir join ile birleşik ekran üretemezsin. İki temel yaklaşım var: istek anında ilgili servislerin API'lerini çağırıp sonucu birleştirmek (API composition) veya olaylarla beslenen, önceden birleştirilmiş bir okuma modeli tutmak (CQRS). Sorgunun sıklığı ve karmaşıklığı seçimi belirler.
- Basit, seyrek birleşim; birkaç servisi çağırıp birleştir yeter
- Karmaşık/sık birleşik sorgu; hazır okuma modeli lazım
Öneri: Database-per-service (paylaşılan DB'yi ayır)
Servislerin tek bir veritabanını paylaşması gizli ve tehlikeli bir bağımlılıktır: bir servisin şema değişikliği diğerlerini kırar, tablolar üzerinde kimin sorumlu olduğu belirsizleşir ve bağımsız deploy imkânsızlaşır. Her servis kendi verisine sahip olmalı.
Öneri: Saga deseni (dağıtık transaction'dan kaçın)
Birden çok servisi kapsayan işlemi tek bir dağıtık transaction (2PC) ile atomik yapmaya çalışma; 2PC lock tutar, tek nokta kırılganlığı ve düşük kullanılabilirlik getirir. Bunun yerine akışı, her biri kendi servisinde yerel transaction olan adımlara böl ve hata olursa telafi (compensation) adımlarıyla geri al.
Öneri: Transactional outbox (ikili yazma tuzağı)
Bir servis hem kendi DB'sine yazıp hem de bir olay yayınlamak istediğinde, ikisini ayrı ayrı yapmak 'ikili yazma' tuzağıdır: biri başarılı, diğeri başarısız olursa sistem tutarsız kalır. Çözüm; olayı iş verisiyle AYNI yerel transaction içinde bir outbox tablosuna yazıp sonra ayrı bir süreçle yayınlamaktır.
Öneri: API composition (istek anında birleştir)
Birleşik veri gereken yerde, çağıran taraf (ör. bir gateway/BFF) ilgili servislerin API'lerini çağırır ve sonuçları bellekte birleştirir. Basit ve az sayıda servisi kapsayan birleşimler için en sade çözümdür; ek veri kopyası tutulmaz.
Öneri: CQRS okuma modeli (olaylarla beslenen)
Karmaşık ve sık birleşik sorgular için, servislerin yaydığı olaylarla önceden birleştirilmiş, sorguya optimize bir okuma modeli (materialized view/denormalize kopya) tutulur. Yazma tarafı servislerde kalır; okuma tarafı hazır veriyi tek yerden hızlıca sunar.