ادامه از : Entities, Value Objects, Aggregates and Roots
وقتی از اصول DDD پیروی کنید، در نهایت یک کلاس پایه برای همه موجودیت های دامنه ایجاد می کنید. این ایده خوبی است زیرا به شما امکان می دهد منطق مشترک را در یک مکان جمع آوری کنید. وقتی تصمیم به انجام آن میگیرید، ناگزیر با این سؤال مواجه میشوید که دقیقاً چه چیزی باید در آن موجودیت پایه گنجانده شود و چگونه باید ارائه شود.
اغلب توسعه دهندگان از interfaceها به عنوان موجودیت پایه(base entity) استفاده می کنند. کد آن ممکن است شبیه به این باشد:
//Interface as a base entity public interface IEntity { Guid Id { get; } }
اینترفیس IEntity فقط یک ویژگی Id را با نوع کلید اصلی مشخص شده تعریف می کند که در مثال بالا Guid است. این می تواند انواع دیگری مانند string، int، long یا هر آنچه شما نیاز دارید باشد.
در حالی که این رویکرد تضمین می کند که همه موجودیت های دامنه دارای حداقل property هستند (ویژگی ID در این مثال)، در بیشتر موارد داشتن یک اینترفیس به عنوان موجودیت پایه، ایده بدی است.
استفاده از کلاس پایه به جای یک interface می تواند ایده بهتری باشد.
اما چگونه کلاس های پایه را پیاده سازی کنید
خوب، اما در base domain class به چه منطقی نیاز داریم؟
بدیهی است که باید یک فیلد Id داشته باشد که به کلید اصلی جدول نگاشت می شود. همه جداول در پایگاه داده باید دارای شناسه هایی با یک نوع باشند تا بتوانیم ویژگی Id را در base class قرار دهیم
بنابراین، base domain class چگونه باید باشد؟ بیایید آن را مرور کنیم.
ما تمام property های رایج را در این کلاس Abstract BaseEntity نگه می داریم. دلیل آن استفاده مجدد از آن در هر موجودیتی است.
public abstract class BaseEntity<T> { public virtual T Id { get; set; } }
شما می توانید برخی از property های دیگر را نیز وارد کنید، با این حال، برای سادگی فعلا، من فقط Id را نگه می دارم. استفاده از نوع T گزینه ای را برای پویا کردن شناسه می دهد. برای مثال، ممکن است بر اساس الزامات جدول، شناسه را به صورت int، bigInt ، Guid و یا هر آنچه شما نیاز دارید باشد.
یکی از بدترین تخلفاتی که برنامه نویسان تازه کار انجام می دهند، داشتن public ID setters مانند قطعه کد فوق است.
فقط زمانی که نیاز به موجودیتهایی با انواع Id مختلف در single project/bounded context ایجاد میشود، باید یک پارامتر نوع را در کلاس پایه Entity معرفی کنید.
موجودیت ها مطابق زیر از Entity<TKey>
class مشتق می شوند:
public class Book : BaseEntity<Guid> { public string Name { get; set; } public float Price { get; set; } }
اگر نوع شناسه موجودیت شما Guid است، چند نکته خوب برای پیاده سازی آن وجود دارد:
خوب است که یک شناسه معتبر روی موجودیت حتی قبل از ذخیره آن در پایگاه داده داشته باشید.
Guid.NewGuid() Method
برای تنظیم Id استفاده نکنید! از یک IGuidGenerator
Custom service هنگام ارسال شناسه از کدی که موجودیت را ایجاد می کند استفاده کنید. این سرویس می بایست برای تولید GUID های متوالی بهینه شده باشد، که برای clustered indexes در پایگاه های داده رابطه ای حیاتی است.چرا GUID را ترجیح می دهیم؟
* در تمامی database providers قابل استفاده است.
* این اجازه می دهد تا بدون نیاز به یک رفت و برگشت برای تولید مقدار ID در پایگاه داده، کلید اصلی را در سمت client تعیین کنید. این می تواند هنگام درج رکوردهای جدید در پایگاه داده کارایی بیشتری داشته باشد و به ما امکان می دهد قبل از تعامل با پایگاه داده، PK را بشناسیم.
* حدس زدن GUID ها غیرممکن است، بنابراین در برخی موارد در مقایسه با مقادیر auto-increment Id، ایمن تر هستند.
در حالی که برخی از معایب نیز وجود دارد.
* مهمترین مشکل GUID این است که به طور پیش فرض ترتیبی نیست. وقتی از GUID به عنوان کلید اصلی استفاده میکنید و آن را بهعنوان clustered indexes (که پیشفرض است) برای جدول خود تنظیم میکنید، مشکل عملکردی قابل توجهی در درج ایجاد میکند (زیرا درج رکورد جدید ممکن است نیاز به ترتیب مجدد رکوردهای موجود داشته باشد).
اجازه ندهید پایگاه داده مسئول برنامه شما باشد.
شناسه های خود را در سطح Application ایجاد کنید نه در سطح Persistence.
اما از نگاه DDD مشکل این که پایگاه داده شناسه های موجودیت شما را تولید کند چیست ؟
مشکل سازترین چیز این است که یک جنبه مهم از دامنه خود را به نرم افزار شخص ثالث واگذار می کنید.
اما بر اساس آنچه که در بالا گفته شد برای BaseEntity خواهیم داشت :
public abstract class BaseEntity<T> { protected BaseEntity() { } public T Id { get; private set; } public BaseEntity(T id) { Id = id } }
و یک موجودیت نمونه بصورت زیر خواهد بود :
public class Book : BaseEntity<Guid> { public string Name { get; set; } public float Price { get; set; } public Book(Guid id) : base(id) { } }
مثال استفاده در یک application service :
public class BookAppService : ApplicationService, IBookAppService { private readonly IRepository<Book> _bookRepository; public BookAppService(IRepository<Book> bookRepository) { _bookRepository = bookRepository; } public async Task CreateAsync(CreateBookDto input) { await _bookRepository.InsertAsync( new Book(GuidGenerator.Create()) { Name = input.Name, Price = input.Price } ); } }
خوب در این لینک یک پیاده سازی نمونه از IGuidGenerator
Custom service را می توانید مشاهده کنید.
بیشتر بخوانید: Entities, Value Objects, Aggregates and Roots
بیشتر بخوانید : Implementing DDD - Clean Architecture
بیشتر بخوانید : لایه Domain در طراحی دامنه گرا DDD
بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Core