Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Domain Driven Design – Stratejik ve Taktiksel Olarak Derinlemesine İnceleme

Merhaba,

Yeni bir projeye başlanacağı vakit projenin toplam net süresini öngörebilmek için kâh sezgisel kâh tahmine dayalı alt kırılımlar oluşturmaya çalışırız. Bu kırılımlar eşliğinde projenin anatomisini periyodik olarak daha hesaplanabilir hale getirerek, izafi açıdan göz önüne sermiş oluruz. Tabi genel anlamda bu uğraşta ne ölçüde başarılı olduğumuz ayrı bir konu olsa da burada niyetimiz stratejik açıdan projenin cephesel haritasını tam olarak masaya yatırabilmektir. Özellikle büyük projelerde, projenin kapsamından dolayı(ki bu kapsamı yazımızın devamında alan, etki alanı ya da domain şeklinde nitelendireceğiz) kaynaklanan bilgi eksikliği nedeniyle bazen zamanı tahmin etmek neredeyse imkansız hale gelebilmektedir. Bu durumda, gerçekte ne yapmamız gerektiğini ve nasıl yapacağımızı tam olarak bilemeyeceğimizden dolayı izafi olarak tahmin edilen zaman ya haddinden fazla uzun ya da kısa olabilmektedir. İşte bu öngörülebilirliği kuvvetlendirmek ve ilgili projenin kapsamındaki bilinmeyen terminolojik terimlerin ve bilgilerin tüm personeller tarafından anlaşılabilir hale getirebilmek için bir standart geliştirilmesi gerekmektedir.

Hadi diyelim ki, üç aşağı beş yukarı öngörülerle temellendirilmiş ve belirli bir noktaya kadar getirilmiş bir projede bazen de hiç öngörülemeyen ekstra işlere ya da modüllere sonradan ihtiyaç duyulabilmektedir. İşte böyle bir durumda geliştiriciler açısından önceden yazılmış kodun ne yaptığını hatırlamak ve hatta tekrardan anlamak neredeyse imkansız hale gelebilmektedir. İşte bu durumda da yazılımcılar açısından kodu hızlı hatırlayabilmek ve bir standart haline getirebilmek için başlangıçta uygun kuralların getirilmesi ve o kurallar çerçevesinde kodun geliştirilmesi gerekmektedir.

Yukarıda bahsedilen proje süreçlerinde yaşanan sıkıntıları özetlersek eğer; proje kapsamından kaynaklı olası terminoloji ve bilgi eksikliği yüzünden meydana gelen hesaplamadaki tutarsızlıklar yahut geliştiriciler açısından yaşanabilen olası kod karmaşası ve geriye dönük mimarisel ya da tasarımsal kopuşlar dikkat ederseniz hep projelerin etki alanıyla ilgili çözüm getirebileceğimiz noktalara temas etmektedir. İşte tüm projelerde, işin kavramsal mantığını ve etki alanını geliştirici olanlar için koda yansıtan, geliştirici olmayanlar için ise daha anlaşılır ve sürdürülebilir kılan en azından olayı bu kuramsal açıdan değerlendirmemizi savunan Domain Driven Design yaklaşımını baz alarak bu sorunlara kolayca çözüm bulabilir ve ortadan kaldırabiliriz, kaldırmayı amaçlayabiliriz 🙂

Bu içeriğimizde Domain Driven Design(DDD)’ın stratejik açıdan ne olduğunu, neleri savunduğunu detaylıca inceleyecek ve ardından taktiksel açıdan DDD’nin hangi terimleri günlük hayatımıza kazandırdığını ve mimarilerimize ne gibi şekiller verebildiğini tartışıyor olacağız. Buyrun başlayalım…

Domain Driven Design Nedir?

Domain Driven Design, gerçek dünyadaki iş modellerini herkesin anlayabileceği ortak bir dil(Ubiquitous Language) ile oluşturarak dijital dünyaya uyarlamak için yazılımların nasıl modellenmesi gerektiği konusunda bir felsefeyi savunur.

Domain Driven Design’ı anlayabilmek için öncelikle ‘Domain’ kelimesinin ne olduğunu anlamamız gerekmektedir. Mevzu bahis konumuzun kitabının yazarı olan Eric Evans’a göre Domain;

Her yazılımın kendine has olayları ve ilgi alanları vardır.

İşte buradaki ilgi alanı Domain’e karşılık gelmektedir. Bunu şöyle örneklendirebiliriz: Bir market yönetim sistemi inşa edeceğimizi düşünelim. Bu işi “Market Yönetimi” şeklinde nitelendirmek sizce doğru mu? Bazılarınız evet diyebilir ama gerçekten öyle mi? Misal, kurulacak sistem bir süper market için ise bu marketten farklı olacaktır. Öyle değil mi? Bu yüzden ilgi alanımızı isimlendirirken öncelikle spesifik/özel olmaya dikkat etmeli ve yapacağımız işe uygun bir isimlendirmeyle başlamalıyız. Evet, eğer bu bir süper market yönetim sistemiyse o halde “Süper Market Yönetimi” şeklinde isim vermemiz “Market Yönetimi” isminden daha mantıklı, açıklayıcı ve anlamlı olacaktır. Böylece yazılımımızın Domain’i(yani ilgi/etki alanı) isimsel olarak net belirlenmiş olacaktır.

Domain Driven Design‘ın, ‘Domain’ kelimesine açıklık getirdikten sonra ‘Driven Design’ kısmını da açarsak eğer; proje yaklaşımımızın “tasarım aklı kim” sorusunu soran kısmıdır. Bunu da örneklendirmek için Data Driven Design‘ı ele alalım. Adı geçen yaklaşım, mevcut problemi data/veri temelli bir şekilde analiz etmeye ve çözmeye çalışan bir stratejiye sahiptir. Benzer şekilde ilgili kalıbı -Ahmet Driven Design- olarak kurgularsak var olan problemi Ahmet temelli bir şekilde ele almayı önermektedir. Haliyle Domain Driven Design‘da var olan ve yaşanan problemlerin Domain esas alınarak analiz edilmesi ve çözülmesi gerektiğini savunan ve bunun için Domain’in net bir şekilde anlaşılmasının gerekli olduğunu söyleyen bir felsefedir.

Domain Driven Design, var olan ve yaşanan problemlerin Domain esas alınarak analiz edilmesi ve çözülmesi gerektiğini savunan ve bunun için Domain’in net bir şekilde anlaşılmasının gerekli olduğunu söyleyen bir felsefedir.

DDD’yi öncelikle stratejik açıdan ele alarak teorik yapılanmasını incelemekte fayda olacak kanaatindeyim. O yüzden hiç vakit kaybetmeden stratejik DDD ile yolumuza devam edelim.

Strategic Domain Driven Design

DDD, stratejik olarak temel kavramlar üretmekte ve her bir proje sürecinde bu kavramlara özen gösterilmesi gerektiğini ifade etmektedir. Şimdi gelin bu kavramları tek tek inceleyelim.

Domain Model

Domain Driven Design - Stratejik ve Taktiksel Olarak Derinlemesine İnceleme

Örnek bir Domain Model

Domain Driven Design, uygulamanın etki planını yansıtacak bir diyagram olan Domain Model’in kullanılmasını önermektedir. Bir domain model, yapılacak yazılımın etki alanının(yani domain’inin) toplam fikrini içermesi ve şöyleee bakıldığı zaman herkesin ilgili operasyona dair neyin ne anlama geldiğini anlayacağı bir vaziyette olması gerekmektedir.

Domain model içinde tüm özellik adlarına sahiptir ve genellikle, UML diyagramlarına nazaran resim ve görselle desteklenmektedir.

Son olarak Eric Evans’a göre domain model;

Domain Model, belirli bir diyagram değildir! Diyagramın iletmeyi amaçladığı fikirdir. Ve bu sadece alan uzmanının kafasındaki bilgi değil, bu bilginin titiz bir şekilde organize edilmiş bir soyutlamasıdır.

Domain Expert

Bir yazılımcıya uçak trafiği yönlendirme ile ilgili bir uygulama geliştirmesi söylense, ilgili yazılımcının konuya dair herhangi bir bilgisi olmaksızın(yani domain bilgisi olmadan) uygulama geliştirmesi pek mümkün olmayacaktır. Yazılımcının bu işi gerçekleştirebilmesi için; uçak, uçak trafiği, nereye yönlendirileceği, ne şekilde yönlendirileceği, işin mevzuatı vs. gibi türlü bilgilerin aydınlatılması gerekmektedir. Peki bu konulara hakim olan kim var? Filanca kişi… Artık her kimse… İşte o filanca kişi işin uzmanı ve yazılımın geliştirilmesi için gerekli tüm teorik bilgilere sahiptir. Haliyle bu kişi olmaksızın yazılımın geliştirilmesi pek mümkün değildir. Bu kişi artık bizim için Domain Expert’tir.

UBIQUITOUS Language

Domain Driven Design - Stratejik ve Taktiksel Olarak Derinlemesine İncelemeTelafuzu zor olan bu terim(yubikutıs ya da öyle bişey) yazılım ekibiyle, domain expert arasındaki ortak iletişimi sağlamakta, sağlanması gerektiğini ifade etmektedir. Domain expert’ler, alanlarına dair her ne kadar derin ve yeterli bilgiye sahip olsalar da yazılım geliştirme hakkında hiçbir şey bilmiyor olabilirler. Aynı şey yukarıda gördüğümüz gibi bir yazılım geliştirici için de geçerli olabilir ve çalışılacak alana dair herhangi bir bilgi söz konusu olmayabilir. İşte böyle bir durumda DDD yazılım geliştiricisi ile domain expert’ler arasında her iki tarafında rahat anlaşabilmesi için ortak dil bulunmasını önermektedir. Hatta bu bir tek domain expert ile geliştiricilerden ziyade projedeki diğer tüm çalışanlar içinde geçerli olabilecek şekilde anlaşılabilir bir dil olmasını savunmaktadır. Evet, bu dili geliştirmek kolay değildir. Zaman alabilir amma velakin son derece gerekli bir uğraş olduğu aşikardır. Zaten sizler onlarca kez, bu şekilde ortak bir dil geliştirilmediği durumlar için projelerde anlaşmazlıkların çıktığına ve hatta aynı şeyin farklı kelimelerle anlatılmaya çalışıldığından tartışmaların lüzumsuz yere yükseldiğine onlarca kez şahit olmuşsunuzdur kanaatindeyim. Haliyle bu doğruyu illa ki DDD’nin söylemesine gerek olmasa da aklın yolu bir olduğu için DDD’de bunu kayıtsız şartsız olması gereken bir durum olarak ortaya koymaktadır.

Peki Uniquitous Language’i koda nasıl yansıtacağız?
Ubiquitous language’in önerdiği ortak dilin geliştirilmesi ister istemez domain model’e yansıyacaktır. Çünkü domain model, yapılacak işin omurgasıdır. Haliyle otomatik olarak bu ortak dil koda yansımış olacaktır. Zaten ortak dil kullanmanın en büyük avantajı bu dilin tekrarlı kullanımında dilin zayıf yönlerini belirlememizi sağlamasıdır. Bu durumda domain model hızlı bir şekilde düzeltme yapabilmemizi sağlayacak ve böylece ortak dilde bir şey değişirse bu domain model aracılığıyla doğrudan kod üzerinde de değişikliğe sebebiyet vermiş olacaktır.

Son olarak ortak dilin neden elzem olduğunu ortaya koyabilmek için Barış Velioğlu’nun makalesindeki örneği aşağıda alıntılıyorum:

…dolayısıyla ekip ya da firma içerisinde isimlendirme konusunda tutarlılık göstermek bir yana, yazılımcıların kendileriyle bile çeliştikleri durumlar olur. Bazen Delete sözcüğünü kullanırken, bazen Remove sözcüğünü kullanmaya karar verirler ve sorduğunuzda nedenini kendileri bile bilmez ya da hatırlamazlar…

🙂 Evet, bu güzel izahatten sonra bir projedeki değil farklı paydaşların iletişimi için aynı işi yapan geliştiricilerin bile kendi aralarında ortak bir terminolojiyi benimsemeleri karışıklılığı engellemek için oldukça önem arz etmektedir.

Bounded Context

Domain Driven Design tasarımındaki en merkezi prensip Bounded Context’tir.

Bounded Context, birbirlerinden ayrılmış ve sınırları belirlenmiş yapılanmalardır. Esasında bu içeriğimizin başlangıç paragrafında belirtilen alt kırılımlardaki her bir ana kırılım bir Bounded Context’e karşılık gelmektedir. Bounded Context’leri mikro servis mimarilerdeki her bir servise karşılık gelen projeler/microservis olarak da düşenebilirsiniz. Diğer servisler olmadan, onlara bağlı olmaksızın geliştirilebilen, çalıştırılabilen, bağımsız otonom birimlerdir.

Örnek vermemiz gerekirse; Amazon’da ürünlerin aranıp listelendiği yer, alışveriş sepeti, satın alma işlevleri vs. ayrı ayrı bounded context’tir. Bu işlevlerin her biri diğeri olmadan tek başına geliştirilebilir.

Peki Bounded Context’ler neye göre belirlenir?
Bounded context’leri belirleyebilmek sanıldığı kadar kolay bir iş değildir. Bunun için sezgisel olarak aşağıdaki durumlara dikkat edilmesi önerilmektedir:

  • Domain hakkında konuşurken, konuşulan dile pürdikkat kesilmek. Zaman zaman benzer, hatta aynı kelimeler farklı anlamlar ifade etmeye başlayabilir. Dilin tam bu farklılaşmaya başladığı noktalar, bize farklı sınırlar içerisinde bulunduğumuzu işaret ediyor olabilir.
  • Hangi seviyede transactional tutarlılığa ihtiyaç olduğunu gözlemlemek.
  • Yapılacak işin altyapısına göre değil, iş mantığına göre belirlenmelidir.

Domain Driven Design ile çalışmak heuristic(sezgisel) olabilmeyi gerektirir.

Bounded context’ler;

  • Kendi içlerinde tutarlı ve bütündür.
  • Pek istenmese de belirli kurallar çerçevesinde birbirleriyle iletişim kurabilirler.
Context MAPPING

Bounded context’lerin kendi aralarındaki iletişim mimarisine dayalı birbirleriyle olan kesişim noktalarını izah edebilmeye context mapping denmektedir. Yani bir başka deyişle context mapping, bounded context’ler ile bunlardan sorumlu ekipler arasındaki ilişkiyi belirlemenize olanak sağlayan araçtır. Buna bir örnek vermemiz gerekirse eğer; siparişler tablosunda(bounded context) müşteri numarasının olması amma velakin müşteri tablosunda(bounded context) siparişe dair herhangi birşeyin olmaması yani bu sınırların ayarlanması bir Context Mapping’dir.

Clean – Readable Code

Kod inşa edilirken domainin amacını yansıtacak şekilde inşa edilmelidir. Burada dikkat edilmesi gereken yapılara verilen isimlerin ne kadar pratik ya da standart olduğu değil, uzun dahi olsa ne kadar yaptığı işi iyi anlatıyor olmasıdır. Bu DDD’nin önerisidir. Temiz koda gelirsek eğer, zaten bu iyi bir yazılımın temel gereksinimidir.

Evet… DDD’nin stratejik açıdan daha fazla kavramı olsa da konuyu genel anlamda anlayabilmek için ana hat mahiyetinde yukarıdakilerin yeterli olduğu kanaatindeyim. Şimdi ise olayı biraz daha pratiğe dökelim ve DDD’yi taktiksel açıdan inceleyerek, bizlere pratikte neleri önerdiğini görelim.

Tactical Domain Driven Design

Entity

DDD’da önemli bir kavram olan Entity, kendini diğer nesnelere nazaran tekilleştirebilmek için bir kimliğe(Id) sahip olan nesnelerdir. Entity, özünde Entity Framework’den aşina olunduğu gibi yeryüzündeki herhangi bir şey için modellenmiş nesnelere karşılık gelmektedir. Bahsedilen kimlik ise bu nesnelerin her biri için yaratıldığı süreçten itibaren diğerlerinden ayırmamızı sağlayan ve değişmeden taşınan Id değeridir.

Entity nesnelerinin değerleri değiştirilebilir.

Genellikle bir entity tanımlanırken, uygulamadaki diğer sınıflardan farkını yaratabilmek için ‘IEntity’ yahut ‘BaseEntity’ gibi bir arayüz yahut sınıf aracılığıyla aşağıdakine benzer bir işaretleme gerçekleştirilmektedir.

    public interface IEntity { }
    public class BaseEntity : IEntity
    {
        public int Id { get; set; }
    }
    public class Donanim : BaseEntity { }
    public class Yazilim : BaseEntity { }
Value Object

DDD prensibini kullanan veya kullanmayan çoğu projede value object’ler farkında olunsun ya da olunmasın kullanılan temel DDD yapı taşlarıdır diyebiliriz.

Value object, herhangi bir kimlik(Id) değeri olmayan ve böylece aynı değerlere sahip iki value object nesnesinin değersel açıdan aynı olarak kabul edilebilir olmasını sağlayan ve dolayısıyla birbirlerinin yerine geçebilecekleri anlamına gelen bir nesnedir. İşte bu nedenle value object’ler her daim değişmez(immutable)dirler. (bknz: Records İle Immutable Datalar)

Value object’lerin değişmez olması operasyon süreçlerini hem thread-safe hem de side effect(yan etki) durumlarına karşı korunaklı kılacaktır.

Value object tanımlamalarında ‘ValueObject’ gibi bir isme sahip abstract class’la işaretleme yapılır ve ilgili class içerisinde birden fazla value object karşılaştırmasını yapacak olan aşağıdaki metotlar barındırılır.

public abstract class ValueObject
{
    protected static bool EqualOperator(ValueObject left, ValueObject right)
    {
        if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
        {
            return false;
        }
        return ReferenceEquals(left, null) || left.Equals(right);
    }

    protected static bool NotEqualOperator(ValueObject left, ValueObject right)
    {
        return !(EqualOperator(left, right));
    }

    protected abstract IEnumerable<object> GetEqualityComponents();

    public override bool Equals(object obj)
    {
        if (obj == null || obj.GetType() != GetType())
        {
            return false;
        }

        var other = (ValueObject)obj;

        return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
    }

    public override int GetHashCode()
    {
        return GetEqualityComponents()
            .Select(x => x != null ? x.GetHashCode() : 0)
            .Aggregate((x, y) => x ^ y);
    }
    // Other utility methods
}

Bu metotlar value object’ler için Microsoft tarafından şuradaki makalede de önerilmektedir. Ne olduklarını kısaca izah etmemiz gerekirse eğer;

  • EqualOperator
    İki value object’in birbirleriyle eşit olduklarını kontrol eder.
  • NotEqualOperator
    İki value object’in birbirleriyle eşit olmadıklarını kontrol eder.
  • GetEqualityComponents
    İki value object karşılaştırılırken içlerindeki property’lerin de karşılaştırılması gerekmektedir. Haliyle ilgili property’leri bu abstract metot üzerinden gönderip karşılaştırma işlemi gerçekleştirilmektedir.
  • Equals
    Verilen value object’ler eşit mi kontrol eder.

Tasarlanan bu ‘ValueObject’ abstract class’ını concrete value object’lere uyguladığımızda aşağıdaki gibi ‘GetEqualityComponents’ metodunun implement edilip içerideki property’lerin gönderilmesi gerekmektedir.

    public class Address : ValueObject
    {
        public string City { get; set; }
        public string Country { get; set; }
        protected override IEnumerable<object> GetEqualityComponents()
        {
            yield return City;
            yield return Country;
        }
    }

Peki ORM kullanırken value object’leri bir entity içerisinde nasıl referans edeceğiz?
Bunun için kullanılan ORM’nin yapısı oldukça önemlidir. Misal olarak Entity Framework kullanıyorsanız Owned Entities and Table Splitting value object’leri rahatlıkla entity sınıflarınızda referans edebilirsiniz.

Aggregate Root

Domain Driven Design - Stratejik ve Taktiksel Olarak Derinlemesine İncelemeEntity ve Value Object’leri inceledikten sonra sıradaki önemli yapı Aggregate Root yapısıdır. Aggregate root, birbiri ile alakalı farklı entitylerin bir bütünlük oluşturabilmeleri, tutarlı olabilmeleri ve iş kurallarını ya da akışlarını gerçekleştirebilmeleri için bir arada kullanılması durumudur.

Aggregate root tanımlanırken, uygulamadaki diğer sınıflardan ayırt edebilmek için genellikle ‘IAggregateRoot’ isminde bir arayüzle işaretleme gerçekleştiririz. Benzer şekilde aggregate root’lar özünde bütünsel bir entity olacaklarından dolayı entity’leri temsil eden ‘IEntity’ yahut ‘BaseEntity’ yapılarıyla işaretlenmesi gerekmektedir.

    public interface IAggregateRoot { }

    public class Address : ValueObject
    {
        public string City { get; set; }
        public string Country { get; set; }
        protected override IEnumerable<object> GetEqualityComponents()
        {
            yield return City;
            yield return Country;
        }
    }

    public class OrderItem : BaseEntity
    {
        public int Quantity { get; set; }
        public long Price { get; set; }
        public int ProductId { get; set; }
    }

    public class Order : BaseEntity, IAggregateRoot
    {
        public DateTime OrderDate { get; private set; }
        public string Description { get; private set; }
        public int BuyerId { get; private set; }
        public string OrderStatus { get; private set; }
        public Address Address { get; private set; }
        public ICollection<OrderItem> OrderItems { get; private set; }

        public Order(DateTime orderDate, string description, int buyerId, string orderStatus, Address address)
        {
            OrderDate = orderDate;
            Description = description;
            BuyerId = buyerId;
            OrderStatus = orderStatus;
            Address = address;
            OrderItems = new List<OrderItem>();
        }
    }

Yukarıdaki kod bloğunu incelerseniz eğer, ‘Order’ın ‘OrderItem’ları yönettiğini göreceksiniz. Haliyle aggregate root olan sınıflarda bu yönetilen değerlerin validasyon kuralları başkası dışarıdan/nesne üzerinden müdahale edemeyeceği şekilde aggregate root üzerinden işletilebilmelidir. Bunun için tüm property’lerin set bloğu private yapılarak gerekli değerler kâh constructor’dan kâh farklı bir metottan alınarak kontrollü bir şekilde tarafımızca set işlemi gerçekleştirilmelidir. Misal yukarıdaki çalışmada değerler constructor’dan alındığı için ilgili validasyonlar aşağıdaki gibi işletilebilir:

        public Order(DateTime orderDate, string description, int buyerId, string orderStatus, Address address)
        {
            if (orderDate < DateTime.Now)
                throw new Exception("Order Date cannot be less than current time...");
            if (description.Length < 5)
                throw new Exception("Description cannot be less than 5 characters");

            /*Diğer Validasyon Kuralları*/

            OrderDate = orderDate;
            Description = description;
            BuyerId = buyerId;
            OrderStatus = orderStatus;
            Address = address;
            OrderItems = new List<OrderItem>();
        }

Esasında aynı durum aşağıdaki ‘OrderItem’da örneklendirildiği gibi normal entity’ler içinde geçerlidir diyebiliriz.

    public class OrderItem : BaseEntity
    {
        public int Quantity { get; private set; }
        public long Price { get; private set; }
        public int ProductId { get; private set; }

        public OrderItem(int quantity, long price, int productId)
        {
            if (quantity < 1)
                throw new Exception("Quantity cannot be less than 1");
            if (price < 5)
                throw new Exception("Price cannot be less than 5");

            Quantity = quantity;
            Price = price;
            ProductId = productId;
        }
    }

Yani burada şöyle bir genellemeye varabiliriz ki: DDD; entity, value object ve aggregate root sınıflarındaki alanlara dışarıdan set işlemi yapılmasını önermemekte bilakis içeriden kontrollü bir şekilde bu işlemin validasyon kontrolleriyle beraber gerçekleştirilmesini tavsiye etmektedir.

Peki aggregate root olan ‘Order’ nesnesine ‘OrderItem’ entity’si nasıl eklenecektir?
Bunun için ‘Order’ içerisinde dışarıdan kontrollü bir şekilde bu işlemi yürütecek bir fonksiyon yazılması yeterlidir.

    public class Order : BaseEntity, IAggregateRoot
    {
        .
        .
        .
        public void AddOrderItem(int quantity, long price, int productId)
        {
            OrderItems.Add(new(quantity, price, productId));
        }
    }

Yukarıdaki kod bloğunu incelerseniz eğer ‘AddOrderItem’a verilen parametreler eşliğinde bir ‘OrderItem’ nesnesi üretilmekte ve bu ‘OrderItems’ koleksiyonuna verilmektedir. ‘OrderItem’ gerekli validasyonları constructor’ında işlettiği için iş kuralları dışında kalan herhangi bir veri söz konusu olduğu durumda exception fırlatacak ve böylece tutarlılık sağlanmış olacaktır.

Şimdi tüm bunların dışında aggregate root’lar için iki önemli kısıtlama mevcuttur:
Domain Driven Design - Stratejik ve Taktiksel Olarak Derinlemesine İnceleme

  • Bunlardan ilki ve en önemlisi, DDD’de bir aggregate root bir başka aggregate root’ta ki entity ile iletişime geçmez, geçmemelidir ve onların referansını içermez, içermemelidir! Aggregate root’lar sadece kendi aralarında yani birbirleriyle iletişim kurabilir, kurmalıdır.
  • Bir diğer kısıtlama ise uygulamalardaki repository sınıfları sade ve sadece aggregate root class’ları için oluşturulmalıdır!

 

Layered Architecture

Domain Driven Design, geliştirme süreçleri için herhangi bir teknolojik veya mimarisel seçim yapmaktan ziyade problemin net bir şekilde tanımlanabilmesine yardımcı olmakta ve uygulamayı geliştiren tüm paydaşların arasında ortak bir dil(ubiquitous language) geliştirilmesini öncelik olarak sunmaktadır. Bu mantıkla yola çıkarsak eğer DDD, projeye herhangi bir mimarisel yaklaşımı dayatmamakta, sadece yukarıda gördüğümüz gibi teknik olarak domain katmanının nasıl tasarlanacağını anlatmaktadır. Bu kabulden sonra hangi yaklaşımın ve mimarinin kullanılacağı biz geliştiricilere ve projenin paydaşlarının ortak kararına kalmıştır.

Bu yüzden DDD felsefesinde önerilebilecek iki mimarisel yaklaşımı sizlere sunarak konuyu daha fazla uzatmadan noktalamak isterim.

Ve geldik sona… 🙂 Teorik ve pratik olarak uzun soluklu bu içeriğe okuyarak eşlik ettiğiniz için teşekkür ederim. Bundan sonraki DDD ile birlikte Microservice yapılanmalarına dair temaslarımız olacak konulara zengin bir temel teşkil etmesini umarak yazımızı burada noktalıyorum.

İlgilenenlerin bol bol faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Yararlandığım Kaynaklar
https://barisvelioglu.net/domain-driven-design-kimdir-fe658cb0f6f0
https://www.codeproject.com/Articles/1020932/Domain-Driven-Design-Reflecting-Business-in-the-Do
www.youtube.com/watch?v=_6msA3h2rFQ
https://vaadin.com/learn/tutorials/ddd/strategic_domain_driven_design
https://vaadin.com/learn/tutorials/ddd/tactical_domain_driven_design
https://vaadin.com/learn/tutorials/ddd/ddd_and_hexagonal
https://eksisozluk.com/bounded-context–6710381

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir