تصور کنید یک Domain Model به زیبایی طراحی شده است که از Repository ها برای مدیریت دریافت موجودیتهای Domain از پایگاه داده شما با یک ORM استفاده میکند، به عنوان مثال. Entity Framework، در MVC یا یک کنترلر Web API.
مشکل این است که لایه Presentation به اشیایی با شکلی متفاوت از مجموعه های لایه دامنه شما نیاز دارد.منظور من از شکل های مختلف این است که این لایه ممکن است به داده های ترکیب شده از چندین Aggregate یا فقط بخش هایی از یک Aggregate نیاز داشته باشد.
موجودیت ها ممکن است بخشی از یک دامنه کسب و کار باشند. بنابراین، آنها می توانند رفتار را پیاده سازی کنند و در use case های مختلف در دامنه اعمال شوند. DTO ها فقط برای انتقال داده ها از جایی به جای دیگر استفاده می شوند. به این ترتیب، آنها بدون رفتار هستند.کپسوله سازی به DTOها اعمال نمیشوند.
معماری شما به طور موثر لایه بندی شده است، اما شما به دنبال مکان مناسبی برای تبدیل Aggregates به DTO (اشیاء انتقال داده) میگردید.
مسئولیت یک Repository در تداوم وضعیت Aggregates است، نه به اشتراک گذاشتن وضعیت Aggregates با Presentation Layer. این در شرح وظایف Repository نیست. در شرح وظایف DTO این است که حامل حالت Aggregate به لایه Presentation باشد. با این حال، DTO باید در جایی مونتاژ شود…
مونتاژ کننده DTO اگر در Repository نیست، پس کجا Aggregate(های) خود را به اشیاء مناسب برای لایه Presentation خود شکل می دهید؟ یک اسمبلر اختصاصی DTO مسئولیت نگاشت (مانند Mapper) ویژگی ها را از Aggregate(ها) به DTO دارد.
یک DTO Assembler می تواند در یک Application Service که کلاینت Repository شما است زندگی کند. Application Service "از Repository برای خواندن نمونه های Aggregate لازم استفاده می کند و سپس به یک DTO Assembler واگذار می کند تا ویژگی های DTO را ترسیم کند."
The DTO Assembler:
public class PartAssembler { public PartDTO WriteDto(Part part) { var partDto = new PartDTO(); partDto.PartNumber = part.PartNumber; partDto.CreatedBy = part.CreatedBy; partDto.CreatedOn = part.CreatedOn; partDto.UnitOfMeasure = part.UnitOfMeasure; partDto.ExtendedDescription = part.ExtendedDescription; partDto.PartDescription = part.PartDescription; partDto.SalesCode = part.SalesCode; var componentsList = part.Components .Select(component => component.ComponentNumber) .ToList(); var laborSequenceList = part.LaborSequences .Select(labor => labor.LaborSequenceNumber) .ToList(); partDto.ComponentList = componentsList; partDto.LaborSequenceList = laborSequenceList; return partDto; } }
The Application Service:
public class PartCatalogService { private readonly IRepository<Part> _partRepository; private readonly PartAssembler _partAssembler; public PartCatalogService(IRepository<Part> partRepository, PartAssembler partAssembler) { _partRepository = partRepository; _partAssembler = partAssembler; } public PartDTO GetPart(string partNumber) { var part = _partRepository.Get(x => x.PartNumber == partNumber); return _partAssembler.WriteDto(part); } }
اشیاء انتقال داده (DTO) برای انتقال داده ها بین Application Layer و Presentation Layer یا سایر انواع کلاینت ها استفاده می شود.
به طور معمول، یک application service از لایه Presentation (اختیاری) با پارامتر DTO فراخوانی می شود. از اشیاء دامنه برای انجام برخی busines logicها خاص استفاده می کند و (به صورت اختیاری) یک DTO را به لایه Presentation برمی گرداند. بنابراین، لایه Presentation به طور کامل از لایه Domain جدا می شود.
در ابتدا، ایجاد یک کلاس DTO برای هر روش application service می تواند کاری خسته کننده و وقت گیر در نظر گرفته شود. با این حال، اگر به درستی از آنها استفاده کنید، می توانند به شما کمک کند. چرا و چگونه؟
List<User>
, e برگرداند، هر کسی می تواند به رمز عبور همه کاربران شما دسترسی داشته باشد، حتی اگر آن را روی صفحه نمایش نشان ندهید. این فقط در مورد امنیت نیست، بلکه در مورد پنهان کردن داده ها است. application service باید فقط آنچه را که لایه Presentation (یا client) نیاز دارد، برگرداند. نه بیشتر نه کمتراما راه حل چیست؟ مشخص کردن propertyها بهعنوان NonSerialized؟
نه، شما نمی توانید بدانید که چه زمانی باید serialized شود و چه زمانی نباید. ممکن است در یک متد application servic مورد نیاز باشد و در متد دیگر مورد نیاز نباشد. بازگرداندن DTO های ایمن، قابل سریال سازی و طراحی خاص انتخاب خوبی در این شرایط است.
بارگذاری تنبل یا Lazy Load به این معنی است که داده های مرتبط از پایگاه داده زمانی که به navigation property دسترسی پیدا می شود بارگیری می شود.
تقریباً تمام فریمورک های ORM از Lazy Load پشتیبانی میکنند. این یک ویژگی است که اگر روابط بین موجودیتها وجود داشته باشد آنها را از پایگاه داده بارگیری می کند. فرض کنید یک کلاس User به یک کلاس Role اشاره دارد. هنگامی که User را از پایگاه داده دریافت می کنید، ویژگی Role (یا collection) بارگذاری نمی شود و از این بابت اگر از آن استفاده ای هم نداشته باشید بی جهت اطلاعات اضافی ای را بارگذاری نکردید اما هنگامی که برای اولین بار ویژگی Role(navigation property) را می خوانید در اینجا ORM در پس زمینه دیتاهای مرتبط را بارگذاری می کند اماشما متوجه این فرآیند نمی شوید! بنابراین، اگر چنین Entity را به لایه Presentation برگردانید، باعث می شود که با اجرای پرس و جوهای اضافی، موجودیت های اضافی را از پایگاه داده بازیابی کند. اگر یک ابزار سریالسازی موجودیت را بخواند، همه ویژگیها را به صورت بازگشتی میخواند و دوباره کل پایگاه داده شما میتواند بازیابی شود.
اگر از Entities در لایه Presentation استفاده کنید، ممکن است مشکلات بیشتری ایجاد شود. بهتر است در لایه Presentation به مجموعه لایه های domain اشاره نکنید.
بیشتر بخوانید : پیاده سازی Application Services در DDD
بیشتر بخوانید : Implementing DDD - Clean Architecture
بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Core