Merhaba Sevgili Okurlar,
Bu yazımda sizlere bir arhitectural pattern olan Domain Driven Design(DDD)'den bahsedeceğim. DDD aslında çok daha geniş bir konu fakat kısa ve öz bir biçimde bu yazımda bahsetmeye çalışacağım.
DDD karmaşık sorunları modellemeyi sağlayan bir mimari yaklaşımdır. Karmaşıklığı modellemek için domain'i merkezine alır. Domain'in nasıl kullanılacağı üzerine eğilir. Bir bakışta domain uzmanlarının domain'i anlaması için bir mimari modelleme yapar. Geliştiricileri ve domain uzmanlarını ortak bir noktaya getirmeyi hedefler.
DDD'ya ait aşağıdaki kavramlardan bahsedelim:
- The Ubiquitous Language
- Bounded Contexts
- Entities
- Value Objects
- Aggregates
- Domain Events
The Ubiquitous Language
Az öncede dediğimiz gibi DDD domain experts ve developer'leri aynı paydada buluşturmayı hedefler. Bu sebeple domain experts ve developers domain hakkında konuşurken ve birlikte çalışırken kullandıkları ortak dile the ubiquitous language denir. OnOrderCreatedDomainEvent gibi event'ler ortak dile güzel bir örnek. Sipariş oluştuğunda ne yapmanız gerektiğini domain uzmanı ile konuşarak gerekli aksiyonları bu event'de alabilirsiniz. Domain gerekliliğine göre yeni eventler oluşturabilirsiniz. OrderCanceledDomainEvent gibi.
Bounded Contexts
Bounded context karmaşık domain içinde anlamlı, kendi içinde tutarlı, birbirinden bağımsız ya da az bağımlı domain parçacıklarıdır. Her biri domain'in bir problemini çözmek için modellenir.
Bizim örneğimiz bir e-ticaret domaini olarak düşünürsek bu büyük ve complex problemi modellemek için customer, order, product olmak üzere üç bounded context'lerine ayrıdık. Sizin domaninize ve ihtiyacınıza göre logical boundy-domain context değişecektir. Bounded context hakkında detaylı bilgi için bu yazıyı ziyaret edebilirsiniz.
Biz ilk olarak müşteri oluşturuyoruz ve daha sonra ürünler, son olarak da müşteri ve ürünleri eşleştirerek sipariş oluşturuyoruz. Bu üç mantıksal sınır bizim domanimizi çözümlüyor. Biz bu projemizde single repo kullandığımız için ve tek bir API içinde bounded context'leri enpoint olarak ayırdığımız için aşağıdaki şeklide. Siz ihtiyacınıza göre her bir bounded-contexti ayrı bir API yapabilirsiniz. Ayrı repolarda tutabilirsiniz.
Bu örnek projemiz aşağıdaki gibi 4 katmandan oluşuyor. Infrastructure, Domain ve Application ve Presentation. DDD Katmanlarını anlamak için önceki yazımı buradan okuyabilirsiniz.
Entities
Entity'ler domain içindeki bir nesneyi temsil ederler ve id'leri ile birbirlerinden ayrışırlar.
Örneğin customer domain içindeki müşteriyi temsil eden bir objedir. DDD yaklaşımında entity'ler domain logic barındırır. Dilerseniz domain logic'i Domain Service adında ayrı bir katman yazıp bu layer'da da tutabilirsiniz. Aşağıda customer Entity'sinde validateEmail , validatePassword gibi bazı basit domain logic'ler oluşturdum.
Karmaşık bir domain kuralı için ise OrderDomainService adında ayrı bir class yazıp bu Domain Katmanında tuttum. Hangi Lojik Domain Katmanında olmalı? Hangi lojik Application Katmanında olmalı konusunu detaylı incelemek için bu yazımı okuyabilirsiniz.
Value Objects
Value Object'ler domainde oluşturulduktan sonra değişmeyen (immutable), encapsule edilmek istenen bir objeyi temsil eder. Identity'leri yoktur. Eşit olup olmadıklarını ayırt etmek için bütün property'leri kıyaslanır eğer aynı ise bu iki value object aynıdır denir. Aşağıda customer Entity'sin bir parçası olan address Value Object'i görüyorsunuz.
Aşagıdaki örnekte Customer Entity'sinde Address ValueObject'ini görüyoruz. Veritabanıda ise Customer ve Address property'leri flat bir şekilde tutulmuştur. Konu ile ilgili yazıyı buradan okuyabilirsiniz.
Bu encapsule edilmiş objeyi bir event'de başka bir servise de iletebiliriz.
Aggregates
Aggregate'ler basit anlamda birkaç nesneden oluşurlar. Tek bir transaction içinde olmasını gerek duydupumuz Entitiy ve Value Object'lerden oluşur. Aggregate kullanımı genellikle data consistency'i sağlamak ya da bounded context'i belirlemek için kullanılır. Aggregate'lerin tıkpı Enttiy'ler gibi Id'leri vardır.
Aşağıda sipariş ve sipariş detaydan oluşan bir Aggreate'i inceleyelim. Görüldüğü gibi hem Order hem de OrderDetail tek bir transaction içinde tutmak istediğim için birleşik bir yapı oluşturuduk. Veri tabanında ise Order ve OrderDetail nesnelerini ayrı bir tabloda tutulmuştur ve Foreign Key ile birbirlerine bağlanmıştır.
Domain Events
Domain Event'ler DDD'nin bize kattığı en büyük faydalardan biridir. Komplex problemin çözümünü, okunabilirliğini artıran bir yapıdır. Domain içinde oluşan bir aksiyon sonrası başka bir aksiyon almamızı sağlar.
Aşağıda müşteri oluştuktan sonra müşteriye email attığımız örnek bir senaryoyu görüyoruz.
OrderCreatedDomainHandler'da bulunan orderReadytoShipping event'inde ise farklı bir durum söz konusu askenkron olarak Shpping API'ına bir mesaj bıraktığımızı düşünelim. Shipping API de bu mesajı alıp okuyup siparişi kargolamaya hazır hale getirdiğini düşünelim . İşte buradaki gibi eğer bir event bir domain'den diğerine sıçrıyor ise buna Integration Event diyoruz. Domain içinde kalıyorsa Domain Event diyoruz.
Konu ile ilgili GitHub reposunu burada bulabilirsiniz, okuduğunuz için teşekkürler,
Sağlıkla kalın.
Bu yazıyı oluştururken okuduğum faydalı kaynaklar:
https://learn.microsoft.com/en-us/azure/architecture/microservices/model/domain-analysis
https://ec2-3-68-183-64.eu-central-1.compute.amazonaws.com/blog/entities-and-value-objects-diving-deep-into-domain-driven-design/
https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs
https://www.spiceworks.com/tech/devops/articles/what-is-domain-driven-design/
https://redis.com/glossary/domain-driven-design-ddd/
http://mustafabas.me/en/what-is-domain-driven-design-ddd--b-1011
https://khalilstemmler.com/articles/typescript-domain-driven-design/entities/
https://github.com/yerinadler/typescript-ddd-demo-app/blob/master/src/core/Entity.ts ***
https://github.com/stemmlerjs/white-label/blob/master/src/infra/sequelize/index.ts
https://github.com/luizcalaca/ddd-typescript-sequelize-node-api/blob/main/src/infrastructure/database/sequelize.ts
https://smit90.medium.com/sequelize-vs-typeorm-choosing-the-right-orm-for-your-node-js-project-a6f8a0cd2b8c
https://github.com/yerinadler/typescript-ddd-demo-app/blob/master/src/domain/property/Property.ts
https://medium.com/@elijahbanjo/implementing-event-driven-architecture-in-typescript-with-node-js-and-express-eefecadaf95f
https://www.ahmetkucukoglu.com/ddd-domain-driven-design-tactical-patterns ***
https://mbarkt3sto.hashnode.dev/ddd-entity-vs-value-object-vs-aggregate-root
https://ardalis.com/aggregate-responsibility-design/
https://enterprisecraftsmanship.com/posts/entity-vs-value-object-the-ultimate-list-of-differences/
https://github.com/yerinadler/typescript-ddd-demo-app/blob/master/src/domain/application/Application.ts
https://dev.to/shafayeat/typescript-for-domain-driven-design-ddd-6kk ***
https://github.com/zhuravlevma/typescript-ddd-architecture/blob/main/src/cart/cart/domain/entities/cart.entity.ts
Hiç yorum yok:
Yorum Gönder