Domain Driven Design - Tactical Patterns

27.10.2022 | dakika okuma

DDD, karmaşıklığı yüksek yazılımlar için ortaya atılmış bir yaklaşımdır. Bu yaklaşım, yazılımların geliştirilmesi ve yazılımların sürekliliğinin sağlanması aşamalarındaki karmaşıklıkları basitleştirir. DDD, Strategical ve Tactical olmak üzere ikiye ayrılır. DDD'nin felsefesi de özü de strategical pattern kısmındadır. Fakat bu yazının konusu strategical pattern olmayacak. Tactical pattern, mimari kısım ile ilgilenir. Sınıfların, metotların ve bağımlılıkların nasıl olması gerektiği ile ilgilidir. Yani clean architecture diyebiliriz. O zaman başlayalım.

Value Objects

Her türlü özellik için framework'ün bize sunduğu ilkel tipleri tercih ediyoruz. Buna Primitive Obsession deniyor. Örneğin; telefon numarası için string'i tercih edebiliriz. .NET'de string için 10'dan fazla metot var. ToLower, PadLeft vs. Fakat bu metotlardan hangisi bir telefon numarası için bir şey ifade eder? String kullanmak yerine PhoneNumber adında bir sınıf oluşturabiliriz.

Primitive Obsession
Value Objects

Bir value object kendisi ile ilgili business'lara da sahip olabilir. Örneğin; 2FA için telefon numarasının son dört hanesini gösteririz. Bir string üzerinden bu dört haneyi almak için ayrı class ve metot yazabiliriz. Fakat bu şekilde uygulamamız gittikçe kimliksizleşmeye başlar. Bir klasör altına birbirleri ile hiç ilgisi olmayan helper class'lar eklenmeye başlar. Helper class yerine PhoneNumber sınıfına metot ekleyebiliriz.

Helper Classes
Value Objects - Their own business

Bir value object kendisi ile ilgili kuralları, validation'ları içerebilir. Örneğin telefon numarasının doğru formatta girilip girilmediğini nesnenin constructor'ında kontrol edebiliriz.

Value Objects - Validity

Value object, kimliği (id'si) olmayan nesnelerdir. Tüm özellikleri aynı olan iki value object birbirine eşittir.

Value Objects - Equality

Bir value object oluşturulduktan sonra state'i değiştirilemez. Yani immutable'dır. Değiştirmek yerine yeniden bir nesne oluşturmamız gerekir.

Value Objects - Immutability

Bir value object kendi başına yaşayamaz ve bir entity'e ait olmak zorundadır. Örneğin telefon numarası tek başına bir şey ifade etmez ve bir müşteriye aittir.

Value Objects - Belonging to an entity

Bir value object birbiri ile ilişkili birden fazla özelliği gruplamak için de kullanılabilir. Örneğin; para. Para, miktardan (örneğin; 100) ve birimden (örneğin; ₺) oluşur. Bunu value object olarak tasarlamazsak Amount ve Currency özelliği için iki farklı property eklememiz gerekir. Yukarıda bahsettiğim Primitive Obsession durumu ortaya çıkar. Ayrıca parayı formatlamak için yukarıda bahsettiğim gibi helper class'lar yazmak gerekebilir. Bunun doğru bir yol olmadığını daha önce belirtmiştim.

Value Objects - Grouper
Money bir domain'de value object olurken bir başka domain'de olmayabilir. Normalde paranın bir seri numarası yani bir kimliği vardır. Fakat bu kimlik e-ticaret domain'inde hiçbir şey ifade etmezken merkez bankası domain'inde çok önemli olabilir. Bu sebeple para, merkez bankası domain'inde value object olmayabilir.

Entities

Entity, kimliği olan (id'si) olan nesnelerdir. Kimliği aynı olan iki entity birbirine eşittir. Value Object'te ise tüm özellikleri aynı olan iki value object eşittir.

Entities - Equality

Entity'nin oluşturulması için zorunlu olan minimum bilgiler constructor'da alınır. Detay bilgiler farklı metotlar ile alınabilir. Genellikle mutable'dır. ValueObject'in aksine bir entity'nin özellikleri sonrasında değiştirilebilir.

Entities - Mutability

Aggregates & Aggregate Roots

Aggregate de kimliği olan (id'si) olan nesnelerdir. Yani aynı zamanda bir entity'dir. Tek bir transaction içerisinde olması gereken Entity'lerin ve Value Object'lerin kümesidir. Dolayısıyla entity'lerin ve value object'lerin tutarlılığından sorumludur. Aggregate kendisi ile ilgisi olmayan bir entity'yi veya aggregate'i navigation property olarak barındıramaz. Id'lerini referans olarak barındırır.

Aggregates

Domain Services

Domain kurallarını uygulayan servislerdir. Bloklu bir banka müşterisinin hesabından para çekememesi bir domain kuralıdır. Bu kural kesinlikle domain katmanında olmalıdır. Yani Account üzerinden Withdrawal metodu çağrıldığında bu kural işletilmelidir. Fakat Customer ve Account farklı aggregate'lerdir. Account, Customer'ı navigation property olarak barındıramaz. O zaman bu kuralı nasıl işleteceğiz? Withdrawal metoduna CustomerRepository geçirebilir ve kontrollerimizi yapabiliriz.

Domain Services - Impure

Buna Impure Domain Model denir. Çünkü bir domain modeli veritabanından sorgu çekemez. Sadece diğer domain modelleri ile etkileşime geçebilir. O zaman Withdrawal metoduna Customer'ı geçirebiliriz.

Domain Services - Pure

Pure Domain Model haline geldi. Fakat domain kuralları her zaman bu kadar basit olmaz. Çok daha kompleks olabilir, çok daha fazla domain modelleri ile iletişime geçebilir. O zaman ne yapacağız? Bu tür durumlarda kuralları ayrı bir sınıf üzerinde işletebiliriz.

Domain Services

Bu yazıda genel hatlarını çizdiğimiz konuların detaylı örneklerini video'dan izleyebilir ve örnek projeye Github adresimden ulaşabilirsiniz.

ahmetkucukoglu/ddd-bank-app

Bank App with DDD

C#
4
0

Vesselam.

Yazıyı Paylaş

Yorumlar

Ömer Faruk
7 ay önce
Detayları ile oldukça güzel bir yazı olmuş teşekküler
Yanıtla
7 ay önce
Eyvallah. Ben teşekkür ederim.
Yanıtla

Yorum bırak

Yanıtla

Yanıtlamayı iptal et
Bu site reCAPTCHA tarafından korunmaktadır ve Google Gizlilik Politikası ve Hizmet Şartları geçerlidir. Yorumunuz başarılı şekilde gönderildi reCaptcha doğrulanamadı
Muhabbetle ASP.NET Core ile geliştirildi.