Pragmatik DDD: Bounded Context ve Aggregate Yüksek Trafikte
Domain-Driven Design'ın pragmatik uygulaması: bounded context sınırları, aggregate tasarımının kilit ve ölçek etkisi, ubiquitous language ve yüksek trafik sistemlerdeki karşılıkları.
DDD, yüksek trafikle ne alaka?
Doğrudan: yüksek trafikte sistemi ölçekleyen şey, doğru çizilmiş sınırlardır — ve DDD, sınır çizme disiplininin ta kendisidir. Bounded context'ler neyin bağımsız ölçekleneceğini, aggregate'ler neyin tek işlemde kilitleneceğini, domain event'ler neyin asenkron akacağını belirler. Bu yazı DDD'yi seremoni olarak değil, ölçeklenebilirlik aracı olarak ele alır — kitaptaki her deseni değil, sahada para eden çekirdeğini.
Bounded context: ölçekleme birimi
Bounded context, bir iş dilinin ve modelinin geçerli olduğu sınırdır: Katalog'da 'ürün' zengin içeriktir, Sepet'te fiyat+adet, Kargo'da ağırlık+boyut. Bu sınırların ölçek karşılığı somuttur: her context bağımsız ölçeklenebilir birimdir — kampanyada Sepet contextini 20 kopyaya çıkarır, Katalog'u önbellek arkasında 4 kopyada tutarsınız. Sınır ihlali de somuttur: iki contextin aynı tabloya yazması, birinin diğerinin iç modeline sorgu atması — bunlar 'dağıtık monolit' üreten kaçaklardır. Context haritası (hangi context, kiminle, hangi ilişkiyle konuşur: müşteri-tedarikçi, uyumlu katman/ACL) mimarinin gerçek şemasıdır; ekip yapısı da idealde bu haritayla örtüşür.
Aggregate: tutarlılık ve kilit sınırı
Aggregate, 'birlikte tutarlı kalması zorunlu' nesnelerin tek işlem sınırında toplanmasıdır — ve yüksek trafikte en kritik tasarım kararıdır, çünkü aggregate sınırı = kilit sınırıdır. Dev bir Sipariş aggregate'i (sipariş + tüm kalemler + ödemeler + kargolar + iadeler) her güncellemede geniş kilit demektir: eşzamanlı iki kalem güncellemesi birbirini bekler, optimistic lock çakışmaları artar, throughput düşer. Kural: aggregate'i küçük tut — gerçek değişmezlerin (invariant) zorunlu kıldığı kadar büyük, bir milim fazla değil. 'Sipariş toplamı kalemlerle tutarlı olmalı' gerçek bir değişmezdir; 'siparişle iade aynı anda güncellenmeli' çoğu zaman değildir — iade ayrı aggregate olur, ilişki kimlikle (orderId) kurulur, tutarlılık event'le sağlanır.
Karar tablosu: bu ikisi aynı aggregate'te mi?
Soru | Evet ise | Hayır ise |
|---|---|---|
Aynı işlemde değişmezleri var mı? | Aynı aggregate adayı | Ayrı aggregate |
Aynı anda farklı kullanıcılar günceller mi? | Ayrılmayı düşün (kilit çakışması) | Birleşik kalabilir |
Biri diğerinden bağımsız listelenir/ölçeklenir mi? | Ayrı aggregate | Birleşik kalabilir |
Gecikmeli tutarlılık iş açısından kabul mü? | Ayır + domain event | Aynı aggregate + tek işlem |
Domain event: sınırlar arası asenkron dil
Aggregate'ler küçülünce aralarındaki tutarlılık domain event'lere taşınır: SiparişOluşturuldu olayı stok rezervasyonunu, e-posta bildirimini, sadakat puanını tetikler — hepsi kendi hızında, kendi context'inde. Bu, önceki yazılarımızdaki asenkron mimarinin (outbox, idempotent tüketici) iş katmanındaki karşılığıdır: event şeması ubiquitous language ile adlandırılır (teknik 'OrderTableUpdated' değil, işin dilinde 'SiparişOnaylandı'), sürümlenir ve sözleşme olarak yönetilir. Yüksek trafik bonusu: event akışı, okuma modellerini (CQRS projeksiyonları) beslemenin de doğal yoludur.
Uygulama katmanında pragmatizm
Java/Spring dünyasında pragmatik DDD şöyle görünür: context başına modül (Spring Modulith ile sınır testleri), aggregate başına repository (iç nesnelere dışarıdan repository yok), uygulama servisi işlem sınırını çizer, domain servisleri saf iş mantığı taşır. İki yaygın tuzaktan kaçınılır: anemik model (tüm mantığın servislerde, entity'lerin veri torbası olması — DDD'nin şekli kalır, özü gider) ve seremoni DDD (üç tabloluk CRUD modülüne value object + factory + specification yığını — karmaşıklığın kendisi risk olur). Ölçüt hep aynıdır: desenin çözdüğü gerçek bir problem var mı?
İş etkisi: sınırlar, ekip hızı ve ölçek aynı haritada
Doğru context haritasının getirisi üç koldan gelir: ölçek — kampanya günü yalnızca sıcak context'ler büyütülür, fatura odaklı kalır; ekip hızı — sınırlar netse ekipler birbirini beklemeden paralel çalışır, merge çatışmaları ve 'kim neyi bozdu' toplantıları azalır; değişim maliyeti — yeni iş kuralı tek context'e dokunur, regresyon yüzeyi küçülür. Aggregate disiplininin getirisi ise doğrudan performans panosunda okunur: kilit bekleme süreleri ve optimistic lock çakışma oranı düşer.
Sık sorulan sorular
DDD için mikroservis şart mı?
Hayır — bounded context mantıksal sınırdır; modüler monolitte modül olarak yaşar. Fiziksel ayrım (servis), ölçek veya ekip gerekçesi doğduğunda yapılır; sınır zaten çizilmişse bu geçiş dilimlenebilir.
Aggregate ne kadar küçük olmalı?
Gerçek değişmezlerin zorunlu kıldığı kadar büyük. Şüphede kalınca küçük seç: eksik değişmez event'le telafi edilir, dev aggregate'in kilit maliyeti telafi edilemez.
Ubiquitous language gerçekten önemli mi?
Ölçekte evet: iş ile mühendislik aynı kelimeleri kullanmıyorsa gereksinim çevirisi her sprintte hata üretir. Kod, test ve toplantı aynı sözlüğü kullanmalıdır.
Mevcut sistemde DDD'ye nasıl başlanır?
Big-bang modelleme ile değil: en sancılı iş alanı seçilir, dili ve sınırı netleştirilir, ACL (anticorruption layer) ile eski modelden yalıtılır. Strangler fig yaklaşımı burada da geçerlidir.
Pragmatik DDD kontrol listesi
Context haritası çizili; ilişki türleri (ACL, müşteri-tedarikçi) işaretli
Context'ler arası doğrudan tablo/iç model erişimi yok
Aggregate'ler gerçek değişmezlere göre boyutlandı; kilit metrikleri izleniyor
Sınırlar arası tutarlılık domain event + outbox ile
Event isimleri iş dilinde; şemalar sürümlü
Modül sınırları testle korunuyor (Spring Modulith/ArchUnit)
Seremoni denetimi: her desen, çözdüğü problemle gerekçeli
SSH Yazılım, alan modellemesi ve bounded context mimarisini yüksek trafik hedefleriyle birlikte tasarlar. Sınır haritanızı birlikte çizelim.