Oyun Kitabı

CRUD (Create, Read, Update, Delete)'dan CQRS (Command Query Responsibility Segregation)'e: Sorun Kod Değil, Modeldir (Cruddan Cqrse Neden Ayrismak Zorundayiz)

CQRS nedir, CRUD ile CQRS arasındaki fark nedir ve CQRS ne zaman kullanılmalıdır? Tek modelin büyük sistemlerde neden yetmediğini anlatan rehber.

The transition from a shared CRUD model to specialized CQRS models

CRUD neden yıllarca harika çalışır?

CRUD kötü değildir. Eğer ekibiniz üç kişi, tek bir ürün geliştiriyorsanız ve günlük birkaç yüz isteği karşılıyorsanız; Controller → Service → Repository → Database akışı mükemmel bir çözümdür. Hızlıdır, anlaşılırdır, yeni bir geliştiricinin zihninde kolayca yer eder.

İlk gün

Product
  ↓
Controller
  ↓
Service
  ↓
Repository
  ↓
Database

Kırılma kodun kalitesinden değil, ürünün büyümesinden gelir. Başlangıçta yalnızca ürünleri listelersiniz. Altı ay sonra pazarlama filtre ister; satış sıralama ister; operasyon dışa aktarma ister; yönetim dashboard ister. Aynı Product modeli bir anda yirmi farklı amaca hizmet etmeye başlar.

Marketing → filtre
Sales     → sorting
Operations→ export
Management→ analytics + dashboard

                    ↓

              aynı Product modeli

CRUD bozulmadı. Modelin sorumluluğu değişti.

Bunun günlük hayattaki işareti genellikle masum bir endpoint'tir:

Bugün
GET /products → 10 ms

Altı ay sonra
GET /products
  ↓ Category
  ↓ Reviews
  ↓ Seller
  ↓ Campaign
  ↓ Discount
  ↓ Stock
  ↓ Warehouse
  ↓ Shipment
  ↓ Favorites

aynı endpoint, farklı ekranları beslemek için büyür

Junior için kısa sözlük:

📦 Aggregate
İş kurallarını bir arada koruyan domain nesnesidir.

📦 Invariant
Her koşulda doğru kalması gereken iş kuralıdır.

📦 Transaction boundary
Ya hep ya hiç birlikte kaydedilmesi gereken işlemlerin sınırıdır.

📦 Projection
Bir olayı, okuma için hazırlanmış yeni bir görünüme dönüştüren işlemdir.

📦 Read model / DTO
Ekranın ihtiyacı kadar alan taşıyan, okumaya uygun veri görünümüdür.

Bu yazının hikâyesi bu noktadan başlar: CQRS teknoloji değiştirerek değil, farklı niyetleri farklı modellere vererek bu gerilimi azaltır.

CRUD ne zaman darboğaz üretir?

CRUD yaklaşımında okuma ve yazma aynı temsil üzerinde buluşur. Yazma tarafı kuralları, tutarlılığı ve geçişleri korumak ister. Okuma tarafı ise hızlı filtre, küçük DTO'lar, arama, sıralama ve ekrana uygun alanlar ister. Tek model bu ihtiyaçları aynı anda taşımaya çalıştığında iki tür maliyet çıkar.

İlki teknik maliyettir. Basit bir liste ekranı için aggregate ilişkileri yüklenir, gereksiz join'ler oluşur ve veri erişiminin maliyeti büyür. İkincisi bilişsel maliyettir. Bir alanı eklemek, bir ekibin raporunu düzeltirken başka bir ekibin transaction kuralını etkilemeye başlar.

100 kullanıcı
  → tek API + tek model
  → CRUD yeterli

5.000 kullanıcı
  → liste, filtre, rapor, iş akışı
  → modelin niyetleri çatışmaya başlar

100.000 kullanıcı
  → okuma ve yazma yükü asimetrik
  → tek model değişimin maliyetini artırır

Bu akış her projede aynı sayılarda yaşanmaz. Sinyal kullanıcı sayısı değil; modelin artık hangi sorumluluğa hizmet ettiğinin belirsizleşmesidir.

CQS: CQRS'in küçük ama kritik kökü

Bertrand Meyer'in Command Query Separation ilkesi basittir: Bir soru sormak, cevabı değiştirmemelidir.

  • Command, durumu değiştirir; niyet taşır ve değer döndürmek zorunda değildir.
  • Query, bilgi döndürür; gözlemlenebilir durumu değiştirmez.

Bu metod seviyesindeki disiplin, API tasarımını daha öngörülebilir hale getirir. ApproveInvoice bir command'dir; GetInvoiceSummary bir query'dir. UpdateInvoiceStatus ise teknik olarak mümkün olsa da işin niyetini gizler.

CQRS, bu fikri mimari seviyeye çıkarır. Command modeli iş kurallarını ve invariants'ları korur. Query modeli, kullanıcı veya sistemin görmek istediği görünümü üretir. Aynı tabloyu paylaşabilirler; iki veritabanı, Kafka veya Event Sourcing zorunlu değildir.

Tek modelin çatıştığı yer

Bir rezervasyon sistemini düşünelim. Yazma tarafı kapasite, iptal, ödeme ve zaman aralığı kurallarını korumalıdır. Bu nedenle güçlü sınırlar ve transaction gerekir. Yönetim ekranı ise bugün doluluk oranını, lokasyon başına özetleri ve yaklaşan rezervasyonları hızlıca görmek ister.

Bu iki niyeti aynı entity grafiğiyle beslemek genellikle şu naif akışa dönüşür:

GET /reservations
  → Reservation aggregate + Customer + Payment + Availability
  → domain kurallarıyla iç içe sorgu
  → yavaş ve kırılgan liste ekranı

CQRS'in cevabı iki ayrı optimizasyon hedefi koymaktır:

Command: ReserveRoom
  → kapasiteyi doğrula
  → kuralları uygula
  → transaction içinde kaydet

Query: GetDailyOccupancy
  → sadece tarih, lokasyon ve sayıları oku
  → ekrana uygun DTO döndür

Bu ayrım sayesinde yazma modeli doğruluk için, okuma modeli keşif ve hız için gelişebilir. Bu, her query'nin O(1) olacağı anlamına gelmez. Ancak maliyeti, tüm domain grafiği yerine çoğu zaman hedef görünümdeki satır ve alan sayısına yaklaştırır.

CQRS için karar sinyalleri

CQRS moda olduğu için seçilmez. Aşağıdaki sinyaller aynı bounded context içinde birlikte görülmeye başladığında anlamlı hale gelir:

  1. Görev tabanlı arayüz: Kullanıcılar Create, Update formundan daha fazlasını yapar; onaylar, rezervasyon yapar, teslimatı başlatır veya geri ödeme ister. Command isimleri iş dilini taşımalıdır.
  2. Asimetrik yük: Okuma trafiği yazma trafiğinden belirgin biçimde yüksektir ve sorgu ihtiyacı domain modelin şeklinden farklıdır.
  3. Yoğun iş kuralı: Aggregate birden çok invariant korur; basit veri güncellemesi artık iş kararını anlatmaz.
  4. Bağımsız değişim ritmi: UI ve raporlama ihtiyaçları, yazma tarafındaki kurallardan daha hızlı değişir.
  5. Açık bounded context: Ayrımı uygulayacak alanın dili, sahipliği ve başarı ölçütü nettir.

Bunlar bir kontrol listesi değil, karar için kanıttır. En güçlü sinyal genellikle şudur: ekip, aynı entity üzerinde hem davranışı hem görünümü değiştirmek için sürekli pazarlık yapıyorsa model iki farklı işi yapıyordur.

CQRS ne değildir?

CQRS'i gereksiz yere pahalı hale getiren beş yanlış eşleştirme vardır.

  • CQRS, tüm sistemi yeniden yazmak değildir. Sadece karmaşık bounded context'te başlayabilir.
  • CQRS, iki fiziksel veritabanı değildir. Önce kodda mantıksal ayrım yapılabilir.
  • CQRS, Event Sourcing değildir. Birlikte kullanılabilirler ama birbirlerinin ön koşulu değildir.
  • CQRS, mesaj broker'ı zorunluluğu değildir. In-process handler'lar başlangıç için yeterli olabilir.
  • CQRS, her ekranı microservice'e dönüştürmek değildir.

Özellikle basit yönetim panellerinde, sığ iş kurallarında ve düşük değişim hızında klasik CRUD daha iyi bir seçimdir. Daha az parça, daha az operasyon, daha kolay onboarding demektir. Mimari olgunluk, CQRS'i ne kadar hızlı eklediğinizle değil; ne zaman eklememeyi bildiğinizle ölçülür.

Trade-off: ne kazanır, ne ödersiniz?

Kazanç Bedel
İş niyetini taşıyan command'ler Daha fazla model ve sözleşme
Kullanım senaryosuna uygun hızlı query'ler Tekrarlanan verinin yönetimi
Okuma ve yazmanın bağımsız evrimi Gözlemlenmesi gereken ek akışlar
Asimetrik yükte ölçekleme seçeneği Fiziksel ayrımda nihai tutarlılık

Bu nedenle CQRS kararı bir framework seçimi değil, maliyet fonksiyonudur: eklediğiniz yapısal karmaşıklık, kaldırdığınız domain ve operasyon karmaşıklığından küçük kalmalıdır.

Başlamak için güvenli yol

En güvenli geçiş, fiziksel ayrımla değil mantıksal ayrımla başlar. Command ve query adlarını iş diline göre ayırın. Query'leri ekrana uygun DTO'lara indirin. Command tarafında doğrulama, yetki ve invariant sınırını belirginleştirin. Ölçüm yapın. Ancak kanıtlanmış bir okuma darboğazı veya bağımsız ölçek ihtiyacı oluştuğunda read model'i ayrı bir depoya taşıyın.

Bu yaklaşım geri dönüşü zor bir mimari sıçrama yerine, öğrenen bir sistem kurar.

Bir sonraki bölümde bu ayrımın içini açacağız: command ve query pipeline'ları, Mediator davranışları, transaction sınırı ve Outbox seçimi birlikte nasıl çalışır?

En sık karıştırılan kavramlar

❌ CQRS = Event Sourcing
❌ CQRS = Microservice
❌ CQRS = Kafka
❌ CQRS = Event-driven architecture

✓ Bunlar birbirinden bağımsız yaklaşımlardır.
✓ Birlikte kullanılabilirler; birbirlerinin şartı değildir.

CQRS'i önce sorumluluk ayrımı olarak ele alın. Ayrı veritabanı, broker veya event stream ancak ölçülmüş bir ihtiyaca cevap veriyorsa eklenmelidir.

Karar matrisi

Boyut CRUD CQRS
Kod başlangıç basitliği Yüksek Orta
Bakım kolaylığı, basit domain Yüksek Orta
Asimetrik okuma ölçeği Sınırlı Güçlü
Yoğun domain kuralı Zamanla zorlaşır Daha görünür sınırlar
Operasyon maliyeti Düşük Mantıksal ayrımda orta, fiziksel ayrımda yüksek

Bu bir puan kartı değil; bağlamı görünür kılan bir karar aracıdır. Basit domain için CRUD'un yüksek basitliği gerçek bir avantajdır.

CRUD küçük sistemlerin problemi değildir. Tek modelin, farklı niyetleri aynı anda temsil etmeye çalışması problemdir. CQRS bu sorunu teknolojiyi değiştirerek değil, sorumlulukları ayırarak çözer.

SSS

Sık sorulan sorular

"CRUD (Create, Read, Update, Delete)'dan CQRS (Command Query Responsibility Segregation)'e: Sorun Kod Değil, Modeldir" ne anlatıyor?

CQRS nedir, CRUD ile CQRS arasındaki fark nedir ve CQRS ne zaman kullanılmalıdır? Tek modelin büyük sistemlerde neden yetmediğini anlatan rehber.

Ana çıkarım nedir?

CQRS Write → Command → Handler → Repository → Database Read → Query → Handler → Read model / DTO ```

Bu yazı kimler için?

Yazılım mimarisi, teslimat ve üretim kararlarını uygulayan mühendisler ve teknik liderler için.

Ogrenilen Muhendislik Prensipleri

  • CQRS, CRUD'u reddetmek değil; tek modelin iki farklı ihtiyacı artık taşıyamadığını fark etmektir.
  • Mimari ayrım sistem geneline değil, karmaşıklığın yoğunlaştığı bounded context'e uygulanmalıdır.
  • Bir modelin maliyeti, çözdüğü domain karmaşıklığından küçükse değerlidir.

Okumaya devam et

Okumaya devam et

Ilgili yazilar

Ilgili yazilar

Ilgili yazilar

Paylaş