Domain Driven Design (DDD) Nedir?
Bu makalede Eric Evans’ın 2003 yılında ortaya atmış olduğu ve sektörde kabul görmüş Domain Driven Design’ın ne olduğundan ve nasıl uygulanabileceğinden bahsedeceğim. Bu yazıdaki kaynakların çoğunluğu Vaughn Vernon’un ‘Domain Driven Design Distilled’ kitabından alınmıştır. Daha derin bir okuma için kitabı önerebilirim.
Kısaca DDD, öncelikli olarak Bounded Context’in Ubiquitous Language ile modellenmesidir. Evet, cümleyi böyle kurduğumuzda hiçbir şey anlaşılmamış olabilir. Gelin makalenin geri kalanında bu kavramları açıklayalım.
DDD, hem stratejik hem de taktiksel olarak yüksek değer sağlayan yazılımları tasarlamamıza ve uygulamamıza yardımcı olan bir dizi araçtır. DDD sayesinde stratejik geliştirme araçları developer ve ekiplerin, işletme için rekabet açısında en iyi yazılım tasarımı seçimlerini ve implementasyon kararları vermeye olanak tanır.
Strategic Design
DDD’de eğer Strategic Design ile başlamaz ve Strategic Design’i iyi anlamazsak Tactical Design’i efektif bir şekilde uygulamamız mümkün değildir. Bu sebeple DDD’yi anlamak için Strategic Design ile başlamalıyız.
Strategic Design’ın derinliklerine geçmeden önce birazdan DDD’yi anlarken kullanacağımız Bounded Context ve Ubiquitous Language kavramlarını kısaca açıklayalım.
Bounded Context (Sınırlı Bağlam) Nedir?
Domainin belirli bir bağlamlara ayrılır ve her bağlam kendi sınırlarına sahip olur. Kompleks bir domain kendi içerisinde subdomainlere ayrılabilir
Ubiquitous Language (Her Yerde Geçerli Dil) Nedir?:
Business ve developer ekipleri arasında ortak bir dilin gerçeli olmasını sağlar. Business kavramları ve süreçlerinin yazılımda direkt olarak yeri vardır. Business yazılım tasarımına ve kodlarına direkt olarak dokunur.
Business’taki her aksiyonun yazılımda fonksiyon olarak yerinin olmasını ve bu fonksiyon/kavramlardan her bahsedildiğinde ekipteki her bir üye tarafından (business veya developer üyeleri) akılda canlanmasını amaçlar. Domain içerisindeki tüm yapılar bu amaca hizmet ederler.
DDD tamamen domaininizi oldukça açık bir şekilde modellemekten ibarettir. Domain Event’leri kullanmak hem açık bir modelleme yapmaya hem de olan biten işlemleri bilmesi gereken sistemlerle paylaşmayı mümkün kılar. Burada ilgili taraflar local Bounded Context veya remote Bounded Context’ler olabilir.
Bounded Context aslında anlamsal bir bağlam sınırıdır. Bunun anlamı yazılım projelerinin sınırları dahilinde her bileşenin belirli bir anlama sahip olmasıdır.
Bounded Context şirketin önemli bir stratejik girişimi olarak geliştirildiğinde buna Core Domain adı verilir.
Bir yazılım modelinde dilin aldığı yeri düşündüğünde Avrupa’yı oluşturan çeşitli ulusları düşünün. Bu alanın herhangi bir ülkesinde, her ülkenin resmi dili açık ve birbirinden farklıdır. Örneğin; Almanya, Fransa ve İtalya gibi ülkelerin sınırları içerisinde resmi diller belirlidir. Bir sınırdan geçtiğinizde resmi dil değişir. Bounded Context’i çok benzer bir şekilde dil sınırları gibi düşünebilirsiniz. DDD’de ise diller yazılım modeline sahip olan ekibin konuştuğu dillerdir ve dikkate değer bir yazılı dil, yazılım modelinin kaynak kodudur.
Bir ekip birden fazla Bounded Context ile çalışabilir fakat birden fazla ekip tek bir Bounded Context üzeridne çalışmamalıdır. Her Bounded Context için kaynak kodunu ve veritabanı şemasını Ubiquitous Language’i ayırdığımız temiz bir şekilde ayırmalıyız. Acceptance testleri ve unit testleri ana kaynak koduyla birlikte tutulmalıdır.
Strategic Design’in Temelleri
En az iki temel Strategic Design aracına ihtiyacımız var. Bunlardan biri Bounded Context diğeri ise Ubiquitous Language’dir. Bounded Context, stratejik girişimin çekirdek kavramlarını kapsamalı ve bunun dışında kalan kavramları dışarıda bırakmalıdır.
Domain Expert’ler doğal olarak işle alakalı konulara daha fazla odaklanmaktadır. Düşünceleri, işin nasıl işlediği konusundaki vizyonlarına dayanır.
Developerler programlama dilleri ve kullandıkları teknolojilere odaklı olabilirler. Ancak, DDD projesinde çalışan developerların temel stratejik girişimin business odaklarını göremeyecek kadar teknik odaklı olmaktan dikkatle kaçınılması gerekmektedir. Yani bir DDD projesinde developer teknik problemlerden önce business’a odaklanmaktadır. Bu sayede developer kendi Bounded Contex’leri içerisinde yavaş yavaş ve doğal süreçte gelişen Ubiquitous Language’i kucaklayabilmektedir.
Business Karmaşıklılığına Okaklanın, Teknik Karmaşıklılığa Değil
Unutmayın, DDD’yi kullanmamızın asıl sebebi business complexity’dir. Bu yüzden asla domain model’ini olması gerektiğinden daha karmaşık hale getirmeyi istemeyiz. DDD öncelikli olarak business complexity’i anlaşılır ve yönetilebilir hale getirmeyi amaçlar. Developer’in technical complexity’den önce business complexity’e bakması gerekir. Tam da bu yüzden developerler business model’e Domain Expert’ler ile birlikte bakmalıdır.
Hem developerlar hem de Domain Expert’ler dökümanların konuşmaya eğilimini önlemelidirler. En iyi Ubiquitous Language ekibin birleşik zihinsel modelini ortaya çıkaran bir geribildirim döngüsünde ortaya çıkacaktır. Açık iletişim, keşif ve zorluklar ekibin Core Domain hakkında daha çok bilgi edinmesini sağlar.
Core Domain’i isimlerle yalnız bırakmayın, yani Core Domain yalnızca fonksiyonunuzun ve Domain Model’lerinizin isimlerinden ibaret olmasın. Bunun yerine Core Domain’i business modelinin ne gerektirdiğine bakarak senaryolaştırın.
Subdomain Nedir?
Kısaca Subdomain genel business domain’in bir alt parçasıdır.
DDD kullanırken, Bounded Context tek bir Subdomain ile bire bir (1:1) hizalanmalıdır. Yani DDD kullanırken eğer bir Bounded Context varsa hedef olarak o Bounded Context’te bir subdomain vardır.
Context Mapping Nedir?
Context Mapping iki Bounded Context’in bir şekilde eşlenmesidir.
İki Bounded Context’te iki Ubiquitous Language bulunmaktadır ve burada Context Mapping uygulanabilmesi için çeşitli yöntemler vardır. Birazdan bu yöntemlere kısaca değineceğiz. Öncesinde Context Mapping’in neden gerektiğini bir analoji ile anlatalım.
İki takımın birlikte çalışması gerektiğini ama ulusal sınırların ötesinde çalıştıklarını ve aynı dili konuşamadıklarını hayal edin. Ya takımların bir tercümana ihtiyacı olur ya da bir veya her iki takımın da diğerinin dilini büyük ölçüde öğrenmesi gerekir. Bir tercüman bulmak her iki takım için daha az iş olur fakat çeşitli yönlerden maliyetli olabilir. Örneğin, bir takımın tercümanla konuşmak için harcadığı ekstra zamanı bir düşünün, ardından tercümanın ifadeleri diğer takıma ilettiği zamanı. İlk birkaç an için işe yarar ama sonra zorlayıcı hale gelir. Yine de, takımlar bu durumu, yabancı bir dil öğrenip benimsemek ve sürekli olarak diller arasında geçiş yapmak yerine daha iyi bir çözüm olarak bulabilir. Tabii ki, bu sadece iki takım arasındaki ilişkiyi tanımlar. Peki ya birkaç başka takım da dahilse? Benzer şekilde bir Ubiquitous Language’in diğerine çevrilmesi veya başka bir Ubiquitous Language uyum sağlanması durumunda da aynı trade off’lar geçerli olacaktır.
Şimdi gelin beraber Context Mapping çeşitlerini inceleyelim.
Partnership: İki takım arasında bir Partnership (ortaklık) ilişkisi bulunmaktadır. Her bir takım bir Bounded Context’ten sorumludur. İki takımı bağımlı hedefler kümesiyle uyumlu hale getirmek için ortaklık oluştururlar.
Shared Kernel: Ekipler, paylaşacak modelleri konusunda anlaşmalıdır. Paylaşılan kodun, yapılandırmanın ve testin sadece bir ekip tarafından sürdürülebileceği olasıdır. Shared Kernel genellikle ilk olarak düşünülmesi çok zor ve sürdürmesi zor olabilir, çünkü ekipler arasında açık iletişim olmalı ve paylaşılacak modelin ne olduğu konusunda sürekli bir anlaşma sağlanmalıdır.
Customer-Supplier: Customer-Supplier, Supplier’in upstream’de olduğu ve Customer’ın downstream’de olduğu iki Bounded Context ve ilgili ekipler arasındaki bir ilişkiyi tanımlar. Supplier, bu ilişkide söz sahibidir Customer’in ihtiyaç duyduğu şeyi sağlamak zroundadır.
Conformist: Conformist bir ilişki, upstream ve downstream ekiplerinin bulunduğu durumlarda ortaya çıkar ve upstream ekibin, downstream ekibin özel ihtiyaçları destekleme motivasyonu yoktur.
Anticorruption Layer: Anticorruption Layer, en savunmacı Context Mapping ilişkisidir; downstream ekipler, kendi Ubiquitous Language’leri ile upstream olan ekiplerin Ubiqutious Language’leri arasında bir çeviri katmanı oluşturur. Bu katman, downstream modelini upstream modelinden izole eder ve ikisi arasında çeviri yapar.
Open Host Service: Open Host Service, Bounded Context’i bir dizi hizmet olarak erişilebilir kılan bir protokol veya arayüzü tanımlar. Protokol, Bounded Context’le entegre olması gereken herkesin nispeten kolay bir şekilde kullanabilmesi için “açıktır”.
Published Language: Published Language, basit tüketim ve çeviri sağlayarak herhangi bir sayıda tüketici Bounded Context tarafından kullanılabilen iyi belgelenmiş bir bilgi alışverişi dilidir.
Separate Ways: Seperate Ways, bir veya birden fazla Bounded Context ile entegrasyonun çeşitli Ubiquitous Language tüketimi yoluyla önemli bir geri dönüş sağlayamayacağı bir durumu tanımlar.
Buraya kadar DDD’de Strategic Design’in temellerini özetledik. Temelimizi oluşturduğumuza göre DDD’de Tactical Design’in temellerini inceleyelim.
Tactical Design
Tıpkı Strategic Design’da olduğu gibi Tactical Design’e has kavramlar mevcuttur. Tactical Design’i anlarken bu kavramları kısaca açıklayalım.
Entity Nedir?
Entity, bireysel bir şeyi modellemektedir. Her bir Entity, aynı türden veya farklı bir türden tüm diğer Entity’lerden onun özgünlüğünü ayırabileceğimiz benzersiz bir kimliğe (Örneğin; ID) sahiptir. Çoğu zaman, bir Entity değiştirilebilir olacaktır. Yani durumu zamanla değişecektir.
Aggregate Nedir?
Her bir Aggregate, bir veya birden daha fazla Entity’den oluşur ve bu Entity kümesi Aggregate Root olarak adlandırılır. Aynı zamanda Aggregate’ler üzerinde oluşturulmuş Value Object’ler varolabilir.
Value Objects Nedir?
Bir Value Object veya basitçe bir Value, değişmez bir kavramsal bütünü modellemektedir. Model içerisinde Value, sadece bir değerdir. Bir Entity gibi benzersiz bir kimliği yoktur ve eşitlik, Value tipi tarafından kapsanan özniteliklerin karşılaştırılmasıyla belirlenir.
Ayrıca, bir Value Object bir nesne değildir ancak sıklıkla bir Entity tanımlamak, nicelendirmek veya ölçmek için kullanılır.
Her Aggreagete, bir transaction consistency sınırı oluşturur. Bu, tek bir Aggreagte içerisinde kontrol eden işlem veri tabanına gönderildiğinde tüm bileşen parçalarının business kurallarına göre consistent olması gerektiği anlamına gelir.
Domain Event Nedir?
Domain Event, Bounded Context’te gerçekleşmiş business olayın kayıdıdır. (Örneğin; ProductCreated, OrderCreated) Strategic Design için Domain Event’ler çok önemli bir araçtır.
Domain Event türlerini nasıl isimlendirdiğinize dikkat etmelisiniz. Kullanılan isimlendirmeler, modelin Ubiquitous Language’ini yansıtmalıdır. Bu kelimleler, modelde gerçekleşen işler ile dış dünya arasında bir köprü oluşturacaktır. Domain Event’leri iyi iletmek son derece önemlidir.
Domain Event tür isimleri geçmiş bir olayın ifadesi olmalıdır, yani geçmiş zamanlı bir fiil olmalıdır.
Değiştirilmiş bir Aggregate ve Domain Event’ini aynı transaction ile kaydedilmesi önemlidir. Eğer bir object relational mapping aracı kullanılıyorsa, Aggregate bir tabloya ve Domain Event’i bir event store table’a kaydedersiniz, ardından işlemi tamamlarsınız. Eğer Event Sourcing kullanıyorsanız, Aggregate’in durumu tamamen Domain Event’ler tarafından temsil edilir.
Burada bahsedilen aynı transaction içerisinde hem Aggregate hem de Domain Event’i kaydetmek için Outbox Pattern’i kullanabilirsiniz.
Domain Event’iniz event store table’a kaydedikten sonra, ilgilenen herkese publish/produce edilebilir. Bu, kendi Bounded Context’iniz içerisinde ve dış Bounded Context’lere olabilir. Bu, dış dünyaya Core Domain’inize dikkate değer bir olayın meydana geldiğini bildirmenin bir yoludur.
Örneğin bir e-commerce domainini düşünelim; OrderCreated eventi ile bir müşterinin bir ürünü satın aldığını bildiriyorum. Bunun sonucunda kargo sürecinin başlaması gerekmekte ve ilgili domain bu eventi consume ettikten sonra kendi iş kurallarını uygulayabilir.
Event Sourcing Nedir?
Event Sourcing, bir Aggregate örneği için gerçekleşen tüm Domain Event’lerini bu Aggregate’da neyin değiştirildiğinin ve hangi işlerin yapıldığının bir kaydının tutulması olarak tanımlanabilir.
Event store, tüm Domain Event’lerinin eklenmiş olduğu ardışık depolama koleksiyonu veya tablosudur. Event store’un yalnızca append-only olması, depolama mekanizmasını hızlandırır ve bu nedenle Event Sourcing kullanan bir Core Domain’in çok yüksek throughput’a, low latency’e ve high scalability’e sahip olacağını varsayabiliriz.
Buraya kadar DDD’de kullanılan Strategic Design ve Tactical Design’in temellerini, kavramlarını inceledik. Makaleyi bitirmeden önce Event Storming’ten bahsedeceğim.
Event Storming Nedir?
Event Storming, hem Domain Expert’lerini hem de developerları hızlı bir öğrenme sürecine dahil etmeyi amaçlayan hızlı bir tasarım tekniğidir. İsimler ve data yerine, iş ve iş süreçlerine odaklanır.
Bir Event Storming sessionunda Ubiquitous Language’i oluşturan bütün ekip üyeleri bir araya gelir ve Domain’de yer alacak business modellemesini birlikte yapar. Genellikle büyük bir duvar ve beyaz bir tahta üzerinde yapışkan notların yerleştirilmesi şeklinde uygulanır fakat bunun illa yüz yüze yapılması gerekmez.
Event Storming’in nasıl uygulandığını daha detaylıca görmek istiyorsanız, buradan Trendyol ekibinin yaptığı örnek bir session’a göz atabilirsiniz.