Oyun Kitabı
DDD Büyük Sistemlerde Nasıl Çalışır? (Ddd Buyuk Sistemlerde Nasil Calisir)
DDD büyük sistemlerde nasıl ölçeklenir? Bounded Context, Context Mapping, Conway Yasası, modüler monolith, mikroservis sınırları ve Transactional Outbox karar…
Önce yanlış soruyu bırakalım
İlk gün tek bir Book modeli vardı. Sonra satış ekibi fiyat istedi, kargo ekibi teslimat penceresi istedi, pazarlama kampanya bilgisi ekledi. Altı ay sonra aynı sınıf, kimsenin tam olarak anlamadığı bir nesneye dönüştü. Sorun kitabın büyümesi değil; farklı iş niyetlerinin tek modele yüklenmesiydi.
Büyük bir sistemi mikroservislere bölmek, DDD uygulamak değildir. Önce işin hangi bölümlerinin farklı dil, karar ve değişim ritmine sahip olduğunu anlamak gerekir. Aksi halde monolith yerine daha pahalı bir dağıtık monolith üretirsiniz.
Tek model → herkesin ihtiyacını taşımaya çalışır → model gerilimi
Satış bağlamı → fiyat ve müşteri kredisi
Kargo bağlamı → teslimat adresi ve paket
Katalog bağlamı → başlık, ISBN ve metaveri
Bu bölümün sorusu şudur: Aynı iş dünyasında yaşayan modelleri, birbirini kirletmeden nasıl konuştururuz?
Kavramlar ilk geçtiği yerde
📦 Bounded Context
Bir modelin, dilin ve kuralların kendi içinde tutarlı kaldığı açık sınır.
📦 Context Mapping
İki bağlamın veri, güç ve sözleşme ilişkisini bilinçli olarak tanımlama pratiği.
📦 Upstream / Downstream
Veriyi veya sözleşmeyi sağlayan taraf upstream; ona bağımlı tüketen taraf downstream'dir.
📦 Anti-Corruption Layer (ACL)
Dışarıdaki modelin kavramlarını kendi domain'inize sızmadan çeviren katman.
Conway Yasası'nın yalın açıklaması şudur: Organizasyon nasıl iletişim kuruyorsa, yazılım da zamanla o yapıyı yansıtır. Bounded Context bir mikroservis değildir; önce modüler bir monolith içinde ayrı bir dil, sahiplik ve kod sınırı olarak yaşayabilir.
Büyük resim
Şirket
│
┌────────────────────┼────────────────────┐
│ │ │
Katalog Satış Kargo
│ │ │
Book Customer Recipient
│ │ │
└──────── OrderConfirmed event ────────┘
│
Outbox
│
Broker
│
Delivery projection
Bu diyagram fiziksel servis sayısını değil; dilin, sahipliğin ve değişimin hangi sınırda yaşadığını gösterir.
Tek doğru model arayışı neden kırılır?
Bir kitap katalogda başlık, yazar ve ISBN'dir; ödünç vermede raftaki fiziksel kopyadır; satışta SKU ve fiyattır. Hepsini tek Book sınıfında birleştirmek yeniden kullanım değil, farklı niyetleri birbirine bağlamaktır. Aynı gerilim Customer için de görülür: satış ekibi kredi ve fatura bilgisi isterken kargo sadece adres ve teslimat tercihlerini ister.
❌ Unified Customer
creditLimit + invoiceAddress + deliveryWindow + marketingConsent + ...
✓ Sales.Customer
creditLimit + billingProfile
✓ Shipping.Recipient
deliveryAddress + deliveryWindow
Sorun nesne sayısı değildir. Aynı modelin farklı ekipler tarafından farklı sebeplerle değiştirilmesidir. Bu değişiklik maliyeti, etkilenen bağlam sayısı k arttıkça en az O(k) olur; bağımlılıklar çoğaldığında test ve koordinasyon maliyeti daha hızlı büyür.
Bounded Context: sınırı önce işin diliyle çizin
Sınır için teknik katmanları veya veritabanı tablolarını izlemeyin. Şu sinyalleri izleyin:
- Aynı kelime toplantıda farklı anlamlara mı geliyor?
- Bir modelde
if (shipping),if (sales)gibi özel durumlar çoğalıyor mu? - Ekipler aynı değişiklik için sürekli birbirini bekliyor mu?
- Bir alanın sahibi, başarı ölçütü ve değişim ritmi belirsiz mi?
Conway Yasası burada bir uyarıdır: sistemin iletişim sınırları çoğu zaman organizasyonun iletişim şeklini yansıtır. Bir ekibin bağımsız karar vermesi bekleniyorsa, o ekibin model ve deploy sınırı da mümkün olduğunca bağımsız olmalıdır. Bu teknik bir hedef değil, sahiplik kararıdır.
Monolith'te başlayın; mikroservisi sonuç olarak görün
Bounded Context'i doğrulamanın en düşük riskli yolu modüler monolith'tir. Bağlam kendi application/domain/infrastructure bileşenlerine, veri erişimine ve açık sözleşmesine sahip olur; ama dağıtık çağrıların operasyonel maliyetini henüz taşımaz.
Catalog module ── published contract ──► Sales module
Sales module ── domain event ─────────► Shipping module
Her module: kendi dilini, use case'lerini ve sahipliğini korur
Mikroservise geçiş ancak bağımsız ölçekleme, bağımsız deploy, farklı güvenlik sınırı veya gerçek ekip otonomisi gibi kanıtlanmış bir ihtiyaç olduğunda anlamlıdır. Bounded Context = Microservice eşitliği, erken dağıtımın en sık nedenidir.
Context Mapping: entegrasyon tesadüf olmamalı
Bağlamlar arası ilişkiyi API endpoint'leri değil, güç dengesi ve model kirlenmesi belirler.
| Durum | Uygun strateji | Neden |
|---|---|---|
| Legacy model kaotik | ACL | Dış dilin domain'e sızmasını engeller |
| Çok sayıda tüketici | Open Host Service + Published Language | İç modeli değil, sürümlenmiş sözleşmeyi paylaşır |
| Upstream üzerinde etkiniz yok, model temiz | Conformist | Gereksiz çeviri katmanı kurmaz |
| Küçük ve nadiren değişen ortak parça | Shared Kernel, son çare | Koordinasyon maliyetini kabul eder |
ACL örneğinde kargo bağlamı, eski ERP'nin CUST_TIER=7 alanını kendi DeliveryEligibility kavramına dönüştürür. ERP terimi kargo domain'ine girmez. Bu katman ek kod maliyetidir; karşılığında dış sistem değiştiğinde etki tek çeviri noktasında kalır.
Event, Outbox ve nihai tutarlılık
Bir bağlam başka bağlamın veritabanını okumamalıdır. Satış bir siparişi onayladığında OrderConfirmed event'i yayınlayabilir; kargo kendi teslimat görünümünü bundan üretir. Event, senkron çağrıyı tamamen yasaklamaz; ancak bağımsız iş akışında zamansal bağımlılığı azaltır.
Sales transaction
→ Order'ı kaydet
→ Outbox'a OrderConfirmed yaz
→ Commit
Relay → Broker → Shipping consumer → Delivery projection
Broker ve veritabanı aynı ACID transaction'ı paylaşmadığı için Outbox gerekir. Sipariş kaydı varsa yayınlanacak event de vardır; relay yeniden deneyebilir. Tüketici duplicate event alabileceği için idempotent olmalıdır. Nihai tutarlılık bir hata değil, ürünün kabul etmesi gereken görünür bir zaman penceresidir.
Bu tasarımın bedeli
Context sınırları daha fazla sözleşme, gözlemlenebilirlik, versiyonlama ve ekip disiplini ister. Her yeni consumer yalnızca O(1) kod eklemez; dashboard, alarm, retry, sahiplik ve entegrasyon testi de ekler. Bu nedenle en iyi sınır, en çok servis üreten sınır değil; değişimin maliyetini gerçekten düşüren sınırdır.
Yanlış güven duygusu oluşturan eşleştirmeler
❌ Bounded Context = mikroservis
✓ Bounded Context önce dil, sahiplik ve model sınırıdır.
❌ Tek model = tutarlılık
✓ Her bağlamın kendi tutarlı modeli olabilir.
❌ Shared Kernel = yeniden kullanım
✓ Shared Kernel ortak değişim ve koordinasyon maliyetidir.
❌ REST çağrısı = entegrasyon stratejisi
✓ Sözleşme, sahiplik ve hata davranışı stratejidir.
❌ Eventual consistency = hata
✓ Doğru tasarlanırsa bağımsız iş akışının bilinçli trade-off'udur.
Stratejik tasarım kontrol listesi
- Aynı kelimenin farklı anlamları nerede başlıyor?
- Her bağlamın dilinin, sahibinin ve başarı ölçütünün sahibi açık mı?
- Bu sınır modüler monolith içinde önce doğrulanabilir mi?
- Upstream model domain'inize doğrudan sızıyor mu; ACL gerekli mi? Örneğin
ERP → ACL → ShippingakışındaCUST_TIER=7, kargonunDeliveryEligibilitykavramına çevrilmelidir. - Paylaşılan sözleşme geriye uyumlu ve sürümlenmiş mi?
- Sipariş commit edildikten sonra broker çökerse ne olur? Outbox, kaydı ve yayınlanacak event'i aynı yerel transaction'a yazar; relay broker geri geldiğinde güvenle yeniden dener.
- Event gecikmesi, duplicate ve replay için ürün ve operasyon kararları var mı?
Kod yalnızca birkaç satır artmaz; izleme, alarm, retry ve operasyon yükü de eklenir. Kod artışı O(1) gibi görünse de operasyon maliyeti doğrusal büyümez. Servis sayısını değil; bağımsız değişebilme ve hata izolasyonunu ölçün.
Bu yazıdan aklında ne kalmalı
- Büyük sistemlerde tek doğru model yoktur; her Bounded Context kendi iş gerçeğini taşır.
- Mikroservis, stratejik sınırın başlangıcı değil; bazen onun fiziksel sonucudur.
- Context Mapping, entegrasyonun teknik ayrıntısını değil; sözleşme, güç ve model koruma kararını görünür kılar.
- Event ve Outbox, bağlamların bağımsızlığını güvenli biçimde korumak için kullanılır.
Mimari sınır, kodun nerede durduğundan önce, bir kararın hangi dilde ve kimin sorumluluğunda verildiğidir.
Son bölümde DDD'nin production yaşamını inceleyeceğiz: Event Storming, legacy dönüşümü, Anti-Corruption Layer ve değişimi güvenle taşıma.
SSS
Sık sorulan sorular
Bounded Context nedir?
Bir modelin, dilin ve kuralların kendi içinde tutarlı kaldığı açık sınır.
Context Mapping nedir?
İki bağlamın veri, güç ve sözleşme ilişkisini bilinçli olarak tanımlama pratiği.
"Bounded Context = mikroservis" doğru mu?
Bounded Context önce dil, sahiplik ve model sınırıdır.
Bu bölüm neyi sabitler?
Bu bölümün sorusu şudur: Aynı iş dünyasında yaşayan modelleri, birbirini kirletmeden nasıl konuştururuz? Büyük sistemlerde tek doğru model yoktur; her Bounded Context kendi iş gerçeğini taşır. İlk gün tek bir `Book` modeli vardı. Sonra satış ekibi fiyat istedi, kargo ekibi teslimat penceresi istedi, pazarlama kampanya bilgisi ekledi. Altı ay sonra aynı sınıf, kimsenin tam olarak anlamadığı bir nesneye dönüştü. Sorun kitabın büyümesi değil; farklı iş niyetlerinin tek modele yüklenmesiydi.
Ogrenilen Muhendislik Prensipleri
- Bounded Context önce iş dili ve sahiplik sınırıdır; mikroservis olmak zorunda değildir.
- Context Mapping, dış modellerin domain'i kirletmesini engelleyen bilinçli bir sözleşme kararıdır.
- Event ve Outbox bağımsız bağlamlar arasındaki değişimi güvenli ve gözlemlenebilir taşır.
Okumaya devam et
Okumaya devam et
Ilgili yazilar
Production'da DDD: Dağıtık Sistemler ve Modernizasyon Stratejileri
DDD production ortamında nasıl yaşatılır? Event Storming, Saga, Transactional Outbox, Anti-Corruption Layer ve Strangler Fig ile legacy dönüşüm rehberi.
Ilgili yazilar
CQRS (Command Query Responsibility Segregation) Pipeline Nasıl Çalışır? Command ve Query Akışının Anatomisi
CQRS request pipeline nedir? Bir HTTP isteği Controller, MediatR, pipeline behavior, handler, Outbox ve read model üzerinden nasıl ilerler?
Ilgili yazilar
Bir Vertical Slice İçeriden Nasıl Çalışır?
Bir Vertical Slice request'ten doğrulamaya, handler'dan aggregate'e, outbox event'inden read model'e ve bulut maliyetine kadar içeriden nasıl akar? CQRS…