DDD #3 — Modellemeye Başlıyoruz, Bir E-Ticaret Domain’ini Sıfırdan Okumak
Subdomain tipleri, Bounded Context sınırları ve Context Map’e ilk adım. Gerçek bir örnek üzerinden deneyimleme zamanı.
İlk iki yazıda teorinin zeminini oturttuk. DDD’nin neden var olduğunu, Ubiquitous Language’in ne anlama geldiğini, Anemic Domain Model’den neden kaçınmamız gerektiğini konuştuk. Sonra Strategic Design’a girdik: Problem Space, Solution Space, Subdomain tipleri ve Bounded Context kavramlarını tanıdık.
Eğer o yazıları okumadıysan, önce onlara bak. Çünkü bu yazıda her şeyi gerçek bir örnek üzerinden canlandıracağız ve o temeli varsayacağım.
Dürüst olmak gerekirse DDD öğrenmede en tehlikeli yer, “Tamam, kavramı anladım” deyip geçtiğin noktadır.
Çünkü teoride gayet net görünen şeyler, gerçek bir domain’e oturduğunda aniden bulanıklaşır. “Bu bir Core subdomain mı, yoksa Supporting mi?” sorusunun net bir cevabı yoktur, bağlama göre değişir. Önceki makalelerden hatırlayacağın üzere yollar bulanıklaştığında bize yönümüzü Ubiquitous Language gösterecek.
Bu belirsizliği ortadan kaldırmak değil, içinde rahatça düşünebilmek hedefimiz.
Hazırsan başlayalım. ☕️
Domain’imizi Tanıyalım
Bugünkü örneğimiz bir e-ticaret platformu. Pek çoğumuza tanıdık domain’leri konuşalım istedim.
Amazon, Trendyol, Hepsiburada gibi düşünebilirsin. Müşteriler ürün satın alır, satıcılar ürün listeler, siparişler takip edilir, ödemeler alınır, kargolar gönderilir.
Herkesin tanıdığı bir domain. Ve bu tam da istediğimiz şey: kavramlara odaklanabilmek için domain’i kafamızda taşımak zorunda kalmamak.
Ama dikkat et. “Herkesin bildiği” bir domain olduğu için, zihniniz otomatik olarak çözüme koşmaya başlar. “Ürün tablosu şöyle olur, sipariş entity’si böyle olur…”
Dur. Henüz oraya gelmiyoruz.
Önce Problem Space’te kalacağız.
1. Domain’i Okumak
Platformun ne iş yaptığını tek cümleyle tarif edelim:
Alıcılar ve satıcıları bir araya getiren, güvenli ödeme ve lojistik altyapısı sunan bir dijital pazar yeri.
Bu cümle önemli. Çünkü içinde birden fazla iş alanı var: alıcı-satıcı buluşması, ödeme güvenliği, lojistik. Bunların hepsi aynı domain’in içinde ama hepsi farklı problemleri çözüyor.
İşte tam da burada Subdomain analizi başlıyor.
2. Subdomain’leri Belirlemek
Bir e-ticaret platformunun mantıksal iş parçalarını düşündüğümde, şu Subdomain’ler öne çıkıyor:
Catalog (Ürün Kataloğu) Ürünlerin listelendiği, kategorilere ayrıldığı, aranabilir hale getirildiği alan.
Order Management (Sipariş Yönetimi) Siparişin oluşturulmasından teslimata kadar geçen sürecin yönetimi. Siparişin yaşam döngüsü burada yaşar.
Payment (Ödeme) Ödeme alma, iade işlemleri, farklı ödeme yöntemlerinin yönetimi.
Inventory (Stok Yönetimi) Satıcıların ürün stoklarını yönettiği, stok tükenmesi ve rezervasyon işlemlerinin takip edildiği alan.
Shipping (Kargo ve Lojistik) Siparişin kargoya verilmesi, takip edilmesi, teslim süreçlerinin yönetimi.
Seller Management (Satıcı Yönetimi) Satıcıların platforma onboarding’i, performans takibi, komisyon ve anlaşma yönetimi.
Customer Management (Müşteri Yönetimi) Alıcı hesapları, adres yönetimi, sipariş geçmişi.
Notification (Bildirim) E-posta, SMS, push bildirimleri.
Identity & Access (Kimlik ve Erişim) Kayıt, giriş, yetkilendirme.
Güzel. Ama şu an elimizde sadece isimler var. Bunları sınıflandırmadıkça, Domain’i gerçekten okuyamayız.
4. Subdomain Tiplerini Belirlemek
Subdomain tiplerini belirlemek için sormamız gereken asıl soru şu;
Bu platformun asıl rekabet avantajı nerede?
Yani Core Domain ne? Şirketin vizyonunu, can damarını bulmamız gerekiyor.
Şunu düşün: İki e-ticaret platformu var. İkisi de aynı ödeme altyapısını kullanıyor. İkisi de aynı kargo firmasıyla çalışıyor. Peki biri neden daha fazla satış yapıyor? Onu farklı kılan nokta ne olabilir?
Cevap çoğu zaman şurada saklı: daha iyi ürün keşfi, daha akıllı sipariş yönetimi, daha güçlü satıcı ekosistemi.
Şimdi bunu göz önünde bulundurarak sınıflandıralım.
🟢 4.1. Core Subdomain
Order Management ve Catalog
Sipariş yönetimi bu platformun kalbidir. Bir siparişin nasıl oluştuğu, hangi kurallarla ilerleyeceği, hangi durumlarda iptal edileceği, hangi koşullarda iade açılabileceği, bunların hepsi platforma özel iş kurallarıdır. Rakipten satın alınamaz, dışarıya outsource edilemez. Yanlış tasarlanırsa hem müşteri hem satıcı kaybedilir.
Catalog da benzer şekilde kritiktir. Ürünün nasıl kategorize edildiği, arama algoritmasının nasıl çalıştığı, kişiselleştirme kuralları, bunlar doğrudan conversion’ı etkiler. Platformun para kazanma biçimiyle birebir ilişkilidir.
🟡 4.2. Supporting Subdomain
Inventory, Shipping ve Seller Management
Bu üçü Core Domain’i ayakta tutan domain’lerdir. Stok rezervasyonu olmadan sipariş tamamlanamaz. Kargo entegrasyonu olmadan ürün teslim edilemez. Satıcı yönetimi olmadan platform büyüyemez.
Ama dikkat: bunlar domain bilgisi barındırıyor. Satıcı onboarding süreci, bu platforma özel kurallara sahip. Stok rezervasyon mantığı, sipariş akışıyla sıkı sıkıya bağlı. Yani tamamen generic değiller; ama rekabet avantajı yaratmıyorlar.
Customer Management da buraya giriyor. Müşteri adresleri, tercihler, geçmiş siparişler — bunlar platforma özel bilgi barındırıyor ama Core olmayan bir destekleyici alan.
Order Management domain’inin çalışması için gerekli ama kendi kendine yapmasına gerek olmayan işlerdir bunlar.
🔵 4.3. Generic Subdomain
Payment, Notification ve Identity & Access
Ödeme altyapısı, neredeyse her e-ticaret platformunda benzer şekilde çalışır. Stripe, İyzico, Braintree gibi hazır çözümler zaten bu problemi çözmüştür. Platforma özel çok az kural vardır. Dışarıdan satın almak çok daha mantıklıdır.
Bildirim sistemi de benzer. E-posta gönder, SMS at, push bildir… Bu problemi her şirket benzer şekilde çözüyor. Kendi bildirim altyapını yazmak için çok iyi bir neden olmadıkça, bunu generic olarak değerlendirmek doğrudur.
Identity & Access de öyle. OAuth, JWT, SSO. Bunlar çözülmüş problemlerdir.
Şimdi tabloya bakalım:
Bir parantez açmak istiyorum.
Bu sınıflandırma evrensel değildir.
Eğer bu platform “merchant’lara kargo altyapısı satıyor” ve oradan para kazanıyorsa, Shipping bir Core Subdomain haline gelir. Eğer platform “finansal çözümler sunan bir marketplace” ise Payment’ı Generic olarak değerlendirmek yanlış olur.
Subdomain tipini belirleyen şey, şirketin para kazanma modeli ve rekabet stratejisidir.
Bu yüzden DDD kararları asla template değildir. Her zaman şu soruyla başlarsın: “Bu şirket neden var?”
5. Bounded Context’leri Belirlemek
Şimdi Solution Space’e geçiyoruz.
Problem Space’te “ne var” sorusunu cevapladık. Şimdi “bunu yazılımda nasıl sınırlandırırız” sorusuna cevap arayacağız.
Bir önceki yazıda şunu söylemiştim: ideal senaryoda, bir Subdomain’e karşılık bir Bounded Context gelir. Bu greenfield bir proje olduğu için bu ideali hedefleyebiliriz.
Ama dikkat: Subdomain ile Bounded Context aynı şey değildir. Subdomain bir iş problemi alanıdır. Bounded Context ise o problemin yazılımda yaşadığı sınırdır ve bir Ubiquitous Language’ı korur.
Catalog Context
Bu Context içinde konuşulan dil şunları bilir: Product, Category, Variant, Attribute, SearchIndex, Listing.
Burada Product, platformdaki tüm bilgiyi taşıyan, aranabilir, kategorize edilmiş, zengin içerikli bir kavramdır. Fiyatı vardır ama bu fiyat “stok fiyatı” değil, “listeleme fiyatı”dır.
Order Context
Bu Context içinde Order, Product, OrderLine, OrderStatus, ShippingAddress, CancellationPolicy kavramları yaşar.
Dikkat et: burada da bir Product var. Ama bu Product, Catalog Context’teki Product ile aynı nesne değildir.
Order Context’teki Product sadece şunu bilir: o siparişte hangi ürün var, kaç tane, hangi fiyattan. Daha fazlasını bilmesine gerek yok. Catalog Context’teki zengin içerikle, kategorilerle, attribute’larla hiç ilgisi yok.
İşte bu, Bounded Context’in gücü. Aynı kelime, iki farklı Context’te iki farklı anlama geliyor ve bu tamamen doğal.
Inventory Context
StockItem, Reservation, WarehouseLocation, StockMovement burada yaşar.
Bir sipariş oluşturulduğunda, Order Context Inventory Context’e “bu ürünü rezerve et” der. Ama Inventory Context, siparişin detaylarıyla ilgilenmez. Sadece stok kurallarını bilir.
Payment Context
PaymentIntent, Transaction, Refund, PaymentMethod bu Context’e aittir.
Büyük ihtimalle bu Context’in altında bir ödeme sağlayıcısı (Stripe, İyzico gibi) var. Context, dış dünya ile olan bu entegrasyonu kapsüller. Dışarısı değişse bile — farklı bir sağlayıcıya geçilse bile — diğer Context’ler etkilenmez.
Shipping Context
Shipment, TrackingEvent, CarrierIntegration, DeliveryEstimate bu Context’te yaşar.
Order kargoya teslim edildiğinde Shipping Context bir Shipment oluşturur. Kargo firmasıyla konuşur. Tracking olaylarını takip eder. Ama bunların hiçbirini Order Context bilmek zorunda değildir. Order, sadece “kargoya verildi mi, teslim edildi mi” durumunu bilmek ister.
5.6. Seller Context
Seller, SellerContract, CommissionRule, PerformanceMetric burada yaşar.
Bu Context, satıcının platformdaki varlığını ve kurallarını yönetir. Bir satıcının hangi kategorilerde satış yapabileceği, komisyon oranları, performans kriterleri burada belirlenir.
Notification Context
Bu Context neredeyse tamamen generic. NotificationTemplate, DeliveryChannel, NotificationLog gibi kavramlar yaşar.
Diğer Context’ler Notification Context’e “şu kullanıcıya şu bildirimi gönder” der. Nasıl gönderileceğini, hangi kanalı kullanacağını sadece Notification Context bilir.
Identity Context
User, Credential, Session, Role burada.
Bu da büyük ölçüde generic ve dışarıdan satın alınabilir. Ama her Context, kullanıcıyı farklı şekilde görür. Order Context bir Customer görür. Seller Context bir Seller görür. Identity Context ise sadece kimlik doğrulamayı bilir.
6. Context Map’e İlk Adım
Bounded Context’leri belirledik. Ama bu Context’ler birbirinden izole çalışmıyor. Bir sipariş oluşturulduğunda, pek çok şey birbirine bağlı:
Order → Inventory: Ürün rezervasyonu gerekli
Order → Payment: Ödeme alınması gerekli
Order → Shipping: Kargo oluşturulması gerekli
Order → Notification: Müşteriye bildirim gidecek
Catalog → Order: Sipariş oluşturulurken ürün bilgisi gerekli
Seller → Catalog: Satıcı ürün listeliyor
Bu ilişkilerin haritasına Context Map diyoruz.
Context Map, sadece “hangi servis hangi servisi çağırıyor” değildir. Aynı zamanda “bu iki Context arasındaki güç dengesi nedir, dil uyumu nasıl sağlanacak, entegrasyon nasıl yapılacak” sorularına da cevap arar.
İki Context arasındaki ilişkiyi tanımlarken birkaç farklı pattern kullanırız. Bunları gelecek yazıda detaylıca inceleyeceğiz, ama şimdi temel iki kavramı tanıtalım:
Upstream / Downstream
Bir Context diğerini etkiliyor ama diğeri onu etkilemiyorsa, etkileyen taraf Upstream, etkilenen taraf Downstream’dir.
Catalog Context, Order Context için Upstream’dir. Ürün bilgisi Catalog’da belirlenir; Order sadece bu bilgiyi tüketir. Order’ın Catalog’u etkileme imkanı yoktur.
Bu ilişki şunu söyler: Catalog ekibi, Order ekibini beklemeden kararlar alabilir. Ama Order ekibi, Catalog’daki değişikliklerden etkilenir.
Nerelere geldik değil mi :) İşte DDD’nin görünmeyen faydaları.
Anti-Corruption Layer (ACL)
İki Context’in dili farklılaştığında, araya bir “çeviri katmanı” koymak gerekir.
Örneğin Shipping Context, dış bir kargo firmasıyla konuşuyor. O kargo firmasının kendi modeli, kendi terminolojisi var. Order Context bu dili öğrenmek zorunda değil. Araya bir ACL koyuyoruz; o katman çeviriyi yapıyor. Kargo firması değişse bile, Order Context hiçbir şey bilmek zorunda kalmıyor.
Modellemenin Ortasında Duraksayalım
Şu ana kadar şunları yaptık:
Bir e-ticaret platformunu Domain olarak aldık. Problem Space’te kaldık ve Subdomain’leri belirledik. Her Subdomain’in tipini (Core, Supporting, Generic) şirketin rekabet stratejisine bakarak sınıflandırdık. Sonra Solution Space’e geçtik ve her Subdomain için bir Bounded Context tasarladık. Son olarak bu Context’lerin birbirleriyle nasıl konuştuğunu Context Map üzerinden görselleştirmeye başladık.
Ama şunu fark ettin mi?
Henüz tek satır kod yazmadık. Henüz hiçbir entity, hiçbir aggregate, hiçbir servis tasarlamadık. Ama hangi ekip hangi ekibe bağımlı çoktan bulduk bile. Ve unutma ki yazılımda bağımlılıkları doğru şekilde yönetebilmek çok advance bir konudur.
DDD’nin stratejik tasarım kısmını tükettik: doğru soruları sormadan, doğru sınırları çizmeden koda girmemek.
Bu sınırları şimdi doğru çizdiğimiz için Tactical Design aşamasında (entity’leri, aggregate’leri, domain servislerini tasarlarken) çok daha net bir zemine basacağız.
Bir Adım Geri Çekilelim
Bu modellemenin yüzde yüz doğru olduğunu iddia etmiyorum.
Gerçek bir projede bu kararların çoğu domain expert’larla saatlerce yapılan domain discussion’larının sonucunda şekillenir. “Seller Management bir Supporting Subdomain mı yoksa Core mu?” sorusu, ekibin platformun nasıl büyümek istediğine bağlıdır. “Inventory ayrı bir Context mi olmalı, Order Context’in içinde mi yaşamalı?” sorusunun cevabı, sistemin scale ihtiyacına ve ekip yapısına göre değişir.
DDD’de “tek doğru cevap” yoktur. “Bu bağlamda en doğru cevap” vardır.
Modelinin doğru olup olmadığını anlayacağın en önemli işaret şudur: Her Bounded Context içinde dil net mi? Bir kavram o Context içinde tek bir anlama sahip mi? Yoksa bir kavramı açıklarken sürekli “ama bağlama göre değişiyor” mu diyorsun?
Eğer ikincisiyse, Context sınırların bulanıklaşmış demektir.
Sırada Ne Var?
Bu yazıda stratejik tasarımı canlı bir örnek üzerinde pratiğe döktük. Problem Space’ten Solution Space’e geçişi adım adım yürüdük ve Context Map’e ilk adımı attık.
Bir sonraki yazıda Context Map’i derinlemesine inceleyeceğiz. Upstream/Downstream ilişkisi, Shared Kernel, Anti-Corruption Layer ve Published Language gibi pattern’ları gerçek senaryolarla açacağız.
Ve bundan sonra Tactical Design’a adım atacağız: Entity, Value Object, Aggregate ve Domain Event kavramları artık bu Context’lerin içinde yaşamaya başlayacak.
O zamana kadar: Bu e-ticaret modelini kafanda tut. Bir yerde “bu sınıflandırma bana mantıklı gelmiyor” dediğin yer olduysa, bunun üzerine düşün. Neden yanlış hissettirdi? Doğru cevap ne olurdu?
Kaynaklar
Implementing Domain-Driven Design (https://learning.oreilly.com/library/view/implementing-domain-driven-design/9780133039900/)
Buraya kadar benimle kalıp okuduğun için teşekkürler!
Eğer ilham verici ya da düşündürücü bulduysan, aşağıdaki ❤️ ikonuna tıklayabilir, ilgisini çekeceğini düşündüğün biriyle paylaşabilir ve gelecek sayılardan haberdar olmak için abone olabilirsin.
👋 Bağlantıda kalalım! Beni LinkedIn’de bulabilirsin.





