در پست قبلی با کلیات رویکرد Domain-Driven و همچنین مفهوم Domain Model اشنا شدیم. در این مقاله به بررسی برخی مفاهیم جهت پیاده سازی Domain Model در نرم افزار می پردازیم.
- Entity
Entity (به فارسی : موجودیت) به اشیایی گفته می شود که با ID (شناسه ی منحصر به فرد) تعریف و شناخته می شوند. برای مثال “مشتری” در سیستم فروش یک Entity می باشد. Entity در قالب یک کلاس با مشخصه ها (Attributes) و رفتارهای مختلف (Behaviors) پیاده سازی می شود. ویژگی ها و صفات مشتری ممکن است در طول عمر نرم افزار تغییر کند و ویرایش شود، اما ID آن ثابت است. ID می تواند مقداری واقعی و معنی دار مانند شماره ی شناسنامه ی مشتری، و یا مقداری تخصیص شده توسط خود نرم افزار مانند کد اشتراک مشتری باشد. دو Entity مختلف می توانند ویژگی های یکسان داشته باشند بنابراین هر Entity باید متدی برای متمایز کردن خود از سایرین داشته باشد.
نمونه ای ساده از اینترفیس IEntity و موجودیت Custmer :
1 2 3 4 5 6 |
public interface IEntity<TEntity, TID> { TID Id { get; } bool SameIdAs(TEntity entity); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class Customer : IEntity<Customer, long> { public long Id { get; private set; } public string Firstname { get; private set; } public string Lastname { get; private set; } public Customer(long id, string firstname, string lastname) { if (id <= 0) throw new ArgumentException("Id"); Id = id; Firstname = firstname; Lastname = lastname; } public bool SameIdAs(Customer entity) { if (entity != null) { return entity.Id == this.Id; } return false; } . . . } |
نکته : در مثال فوق ویژگی های موجودیت مشتری Encapsulate شده و قابلیت تغییر آنها از بیرون از کلاس وجود ندارد. این تکنیک باعث می شد تا مقادیر Property ها تنها توسط خود Entity تغییر یابد و Entity همیشه در وضعیت معتبر باشد. (می توانید جهت تغییر Property ها متدی مانند Update بر روی کلاس مشتری تعریف نمایید.)
- Value Object
Value Object ها اشیایی هستند که تنها توسط ویژگی ها و مقادیرشان شناخته می شوند و برای سیستم اهمیت دارند. برای مثال “آدرس مشتری” می تواند در قالب یک Value Object طراحی شود. Value Object ها می توانند به Entity های مختلف تخصیص داده شوند و معمولا به صورت Immutable پیاده سازی می شوند.
برای مثال در کد زیر کلاس Address در قالب یک Value Object طراحی شده و در کلاس مشتری استفاده شده است :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Address { public string Street { get; private set; } public string City { get; private set; } public string State { get; private set; } public string BlockNo { get; private set; } public Address(string street, string city, string state, string blockNo) { Street = street; City = city; State = state; BlockNo = blockNo; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class Customer : IEntity<Customer, long> { public long Id { get; private set; } public string Firstname { get; private set; } public string Lastname { get; private set; } public Address Address { get; private set; } public Customer(long id, string firstname, string lastname, Address address) { if (id <= 0) throw new ArgumentException("Id"); Id = id; Firstname = firstname; Lastname = lastname; Address = address; } . . . } |
- Service
برخی عملیات ها در سیستم مربوط به یک شیء خاص نبوده و نمیتوان آنها را در قالب یک شیء و یا رفتاری از یک شیء در نظر گرفت. این نوع عملیات ها در قالب یک Service نوشته شده و در قسمت های مختلف برنامه استفاده می شوند. Service ها به صورت Stateless طراحی می شوند، یعنی هیچگونه وضعیتی را در خود نگهداری نمی کنند. از آنجا که سرویس ها می توانند به ابزارهای زیر ساختی (مانند دیتابیس) و یا وب سرویس های Remote دسترسی داشته باشند، لذا Interface آنها در لایه ی Domain و پیاده سازی واقعی آنها در لایه های دیگر انجام میگیرد تا Domain به دیگر لایه ها وابسته نشود.
سرویس ها در DDD معمولا به ۳ دسته تقسیم می شوند :
Domain Service : سرویس هایی که مربوط به Business و لایه ی Domain نرم افزار می شوند. این سرویس ها معمولا پیاده سازی یک فرآیند، اعتبارسنجی یک فرآیند و یا بازیابی داده ها برای یک فرآیند (از طریق Repository ها) را برعهده دارند. Domain Service ها اغلب به داخل متدها و سازنده ی Entity ها Inject شده و استفاده می شوند.
Application Service : سرویس هایی که با کلاینت های بیرونی ارتباط دارند و پیام ها و دستورات (Commands) را به عملیات ها و پردازش های داخلی Domain تبدیل و اجرا میکنند.
Infrastructure Service : سرویس هایی که با منابع و وب سرویس هایی Remote در ارتباط هستند. ( مانند سرویسی که مسئول ارتباط با وب سرویس ارسال پیامک است)
در این مقاله با ۳ مفهوم Entity، Value Object و Service در DDD آشنا شدیم. در مقاله های بعد به بررسی مفاهیمی مانند Aggregate، AggregateRoot، Bounded Context و … می پردازیم.
سلام …
خیلی ممنون بابت مقاله ی خوبتون … امیدوارم که همینطوری ادامه بدین …
میشه یه مرجع خوب و سریع و به روز برای یادگیری این موضوع (طراحی DDD) معرفی کنین؟
سلام
خواهش میکنم
در هفته های آینده یک مطلب کامل در مورد منابع و همچنین سورس کدهای موجود در اینترنت جهت یادگیری DDD مینویسم.
از نظر من کتاب خود آقای Eric Evans به نام Domain-Driven Design: Tackling Complexity in the Heart of Software و همچنین کتاب Implementing Domain-Driven Design نوشته آقای Vaughn Vernon بهترین منابع موجود هستند.
سلام
مرسی بابت مطابت خوبتون امیدوارم همچنان به اشتراک گذاری دانشتون ادامه بدید
سلام، خواهش میکنم
سلام
درسته که منابعی که گفتین بهترین منابع هستند ولی مطلب فارسی کاملی نداریم. امیدوارم تمام دانشتون در این زمینه رو منتقل کنید.
سلام
درست میفرمایید، منبع فارسی در رابطه با DDD موجود نیست.
من حتما سعی میکنم مبحث رو تا جای عملی و کاربردی پیش ببرم.
خیلی عالی، مشتاقانه منتظر باقی مطالب هستیم.
لطف دارید مهندس
سلام. ممنون میشم اگه مقایسه ای بین domain service و Application Service داشته باشید و وظایف هر کدام را شرح دهید اگر با یه قطعه کد توضیح بدید ممنون میشم.
سلام
Application Service در واقع حاوی Application Logic می باشد و مراحل مورد نیاز مختلف برای انجام یک Use Case را هماهنگ میکند و انجام می دهد. برای مثال در Use Case ثبت نام کاربر، ابتدا Entity را می سازد، سپس آن را به Repository مربوط جهت ذخیره سازی ارسال میکند و سپس سرویس ارسال ایمیل را فراخوانی می کند. ( در اصطلاح فنی، یک Use Case را Orchestrate میکند)
در مقابل Domain Service حاوی Domain Logic می باشد و سرویس هایی در سطح Domain هستند که اغلب رفتارهایی مربوط به Business دارند. کاربردهای مختلفی برای Domain Service می توان نام برد.
۱٫ پیاده سازی یک منطق Reusable و قابل تغییر در سطح Domain. برای مثال سرویس محاسبه ی مالیات می تواند متغیر باشد، به همین دلیل در قالب Domain Service های مختلف نوشته می شود و به صورت Interface در اختیار کلاس های Domain قرار میگیرد.
۲٫ Abstract کردن لایه های زیرساختی از دید کلاس های Domain. برای مثال هنگامی که یک کلاس Domain (به هر دلیلی) به خواندن داده از دیتابیس نیاز دارد، Repository مستقیم به Entity داده نمی شود، بلکه این سرویس در قالب یک Domain Service در اختیار آن قرار میگیرد و Domain Service از Repository (و یا هرمکانیزم دیگری) برای خواندن اطلاعات استفاده میکند.
۳٫ در مواردی که یک فرآیند Domain در قالب یک Entity مدل نمی شود و نیاز است تا عملیات بین چند Entity به عنوان یک رفتار Business مدل شوند.
برای نمونه کد و اطلاعات بیشتر به لینک زیر مراجعه نمایید :
Services in Domain-Driven Design
سلام مهندس
آقا دمت گرم عالی بود.
سلام رضا جان
لطف داری
با سلام و عرض خسته نباشید و تشکر از مقاله خوبتون.
اگه امکانش هست میشه یکم در مورد این متد SameIdAs که در کلاس Customer نوشتید توضیح بدید؟ ممنونم
سلام، خواهش میکنم
از آنجا که برابر بودن دو Entity از طریق Id انها چک می شود، برای صریح بودن این موضوع پیشنهاد می شود از متد جداگانه ای استفاده شود. این متد همچنین در Equals هم صدا زده می شود.
سلام آقای احمدی
سوآلی درمورد object value هایی که بهصورت id در aggrigate root موجود هستن داشتم
سلام سهیل جان
در خدمتتون هستم، سوالتون رو بفرمایید.
آیا رابطه بین user و role که توسط table userRoleMap پیاده سازی میشه
ماننند https://www.mirkosertic.de/media/dddrevised.png
user که لیستی از roles را دارا میباشد
role در user به صورت object value تعریف میشود ؟
نه خیر هر دو Aggregate های جدایی هستند
عرض سلام
داخل گوگل وقتی کلمه onion architecture سرچ میکنیم بعضی عکسها که فکر کنم اشتباه باشه کلاس ریپازیتوری در لایه Domain Service معرفی کردن و عکسهای بیشتری که فکر کنم درست باشه اینترفیس های ریپازیتوری در لایه Domain Service معرفی کردن
نظرتون در این مورد بفرمایید تشکر
سلام
جناب قاسمی عزیز، تعریف Repository (منظور Interface آن) جزئی از Domain شما می باشد. پیشنهاد میکنم برای درک صحیح مفاهیم، کتاب های مرجع Domain-Driven Design را مطالعه بفرمایید. در همه ی کتاب ها مبحثی به نام Repository وجود دارد که به تعریف کامل این موضوع پرداخته شده است.
بسیار عالی
خوبه که مطالبی راجع به DDD در سطح فارسی نوشته میشه
خیلی ممنونم، لطف داری محمد جان
سلام
استاد بسیار لذت بردم
متشکرم
سلام
خواهش میکنم مهدی جان، لطف داری
سلام واقعا عالی بود
دستتون درد نکنه
سلام استاد ببخشید یه سوال برای من بوجود اومد
در یک پروژه که تعداد زیادی Api Service(Infrastructure Service ) وجود دارد
چطور این سرویس ها رو باید پیاده سازی کنیم که بتونیم از این Infrastructure Service ها در لایه UI
استفاده کنیم و دسترسی داشته باشیم
ممنونم
سلام امیرحسین جان.
استفاده از این سرویس ها در UI کار صحیحی نمی باشد.
در واقع اینترفیس Infrastructural Service ها برای استفاده در اختیار لایه های دیگر مثل Application Layer قرار میگیرد.
سلام استاد خسته نباشید ممنونم از راهنمایی تون مرسی
سلام وقتتون بخیر
ببینید ساختار زیر در مورد پیاده سازی یه نرم افزار تیکتینگ درسته :
Aggregate root ی بنام تیکت داریم . هر تیکت می تونه تعدادی گیرنده (Receivers) داشته باشه. گیرنده ها رو به عنوان Value object در نظر گرفته ایم. آیا این درسته ؟
Value Object ی که به صورت یه لیست هست مانند مثال بالا که یه Collection از گیرنده ها برای هر تیکت وجود دارد. و از انجایی که Value Object ها Immutable هستند، در لایه دیتابیس برای مدیریت گیرنده ها باید فقط ثبت و حذف داشته باشیم یا اینکه می تونیم ویرایش هم انجام بدیم؟
سلام یونس جان.
نظر دادن در مورد Domain Model یک مساله و طراحی آن، نیاز به اطلاعات بیشتر از فضای دامنه، Invariant ها و همچنین نیاز مساله و Use Case های مورد نیاز دارد. بنابراین ممکن است راهنمایی من (به علت نداشتن اطلاعات در مورد مساله) درست نباشد. ولیکن یک لیست از Id مربوط به Aggregate گیرنده، می تواند به شکل Value Object بر روی تیکت مدل شود. پیشنهاد می شود از نگه داشتن مستقیم List Id خودداری کنید و یک Value Object جهت Encapsulation بهتر و همچنین مدل کردن بهتر رفتار آن، ایجاد کنید.
پیشنهاد می شود با Value Object ها به صورت Immutable برخورد کنید.