در طراحی دامنه محور (DDD)، business logic اصلی به طور کلی در aggregate ها (Entityها) و
Domain Service ها پیاده سازی می شود. ایجاد یک Domain Service به ویژه زمانی مورد نیاز است که:
منطقی که باید پیاده سازی کنید به بیش از یک aggregate/Entity مربوط می شود، بنابراین به درستی در هیچ یک از aggregate ها قرار نمی گیرد.
سرويس هاي دامنه(Domain Services) برای انجام عملیات دامنه(domain operations) و business rules استفاده می شود.
الگوي Domain Service نشان دهنده رفتارهاست. رفتارهای مربوط به Domain را نشان می دهد.
گاهی اوقات، یک رفتار خاص ممکن است با چندین Entity یا Value Object تعامل داشته باشد. در آن مواقع، یافتن اينکه آن رفتار به کدام موجوديت تعلق دارد دشوار است. وقتی نوبت به چنین سناریویی می رسد،
Domain Service راه حل است.
خوب Domain Services درخواست ها را handle نمی کند. در مورد اجزای UI چیزی نمی داند. انتقال داده ها را "از" و "به" پایگاه داده پياده سازي نمي کند. ورودی کاربر را تایید نمی کند. Domain Service فقط
business logic را مدیریت می کند.
یک Domain Service یک لایه اضافی است که شامل منطق دامنه نیز می باشد. این بخشی از مدل دامنه است، درست مانند Entity ها و Value Objectها. در عین حال، application service لایه دیگری است که حاوی منطق تجاری نیست.
اریک ایوانز در کتاب DDD خود یک سرویس دامنه خوب را در سه ویژگی اساسی توصیف
می کند:
دانش دامنه(domain knowledge) به عنوان دانش یک رشته، حرفه یا فعالیت خاص و تخصصی در مقابل دانش عمومی تعریف می شود. به عبارت دیگر، اصطلاح دانش حوزه برای توصیف دانش متخصصان یا کارشناسان در یک زمینه خاص به کار می رود.
به عنوان مثال، در مهندسی نرم افزار، دانش دامنه می تواند به دانش خاصی در مورد یک محیط خاص که سیستم هدف در آن عمل می کند، اعمال شود.
به طور معمول، کسانی که دانش دامنه دارند در یک زمینه تخصصی کار می کنند. هنگام توسعه نرم افزار برای هر صنعت، مهم است که تفاوت های قابل توجه بین صنایع را تشخیص دهید.
به عنوان مثال، نرم افزار برای یک شرکت مالی به شدت متفاوت از نرم افزار یک شرکت داروسازی است. شرکت های مالی باید نگران اعداد، پیگیری امور مالی و اطمینان از انجام صحیح پرداخت ها و نقل و انتقالات باشند. شرکتهای داروسازی ممکن است نیاز داشته باشند بر بررسیهای قانونی، پزشکی و نظارتی مناسب تمرکز کنند و نرمافزاری که استفاده میکنند باید آن را برای اهداف کیفی در نظر بگیرد.
دانش دامنه برای سازمان ها مهم و ارزشمند است زیرا معمولاً مهارتی هدفمند است که از توسعه دهندگان نرم افزار آموخته می شود. زمانی که یک متخصص دانش حوزه داشته باشد و بتواند آن دانش را به برنامههای کامپیوتری و دادههای فعال ترجمه کند، میتواند نرمافزار را تغییر دهد و از تخصصی بودن آن برای یک زمینه خاص اطمینان حاصل کند و آن را برای کاربران نهایی بسیار ارزشمند میکند.
با توجه به نوع فناوری موجود در بازار این روزها، اطمینان از اینکه نرم افزار به طور خاص برای مخاطبان شما طراحی شده است، می تواند تفاوت را در برابر رقبا ایجاد کند. به همین دلیل است که دانش دامنه یک دارایی با ارزش است.
حوزه دانش(domain knowledge) فعالیتی است که منطق برنامه (application logic) حول آن می چرخد.
آنها دارای منطق دامنه(domain logic) هستند که به یک Entity یا Value Object تعلق ندارد.
یک Domain Service را به عنوان اقدامات/رفتارهایی در دامنه ببينيد که به هیچ موجوديت واحدی تعلق ندارند.آنها business logic شما را پیاده سازی می کنند.
یک Domain Service منطق دامنه را در بر می گیرد(به ویژه، حاوی منطق اعتبارسنجی است) که به طور طبیعی
نمی تواند به عنوان یک Entity یا Value Object مدل شود.
همانطور که در ابتداي پست به آن اشاره شد شما فقط در صورتی نیاز به یک Domain Service دارید که منطق دامنه ای که در حال مدل سازی آن هستید به طور طبیعی روی هیچ Domain Objectی مناسب نباشد. یک سناریوی رایج برای این زمانی است که مسئولیت يک عمل به طور طبیعی بر روی یک شی خاص قرار نمی گیرد یا اگر برای هماهنگ کردن يک عمل به چندین شیء دامنه نیاز داريد.
اکنون که معنای Domain Service را یاد گرفتید، یک مثال عملی خواهید دید و نحوه استفاده از آن را درک خواهید کرد. برخی از گزینه ها نیاز به توضیح دارند، مانند استفاده از Domain Services در داخل application services.
با این حال، Domain Service عموماً باید به عنوان مرحله ای در فرآیندهای دامنه استفاده شوند که کاملاً در مدل دامنه قرار دارند. این منجر به بحث در مورد تزریق Domain Services به موجودیت ها می شود.
خوب FriendOMeter سرویسی در یکDomain Model از یک برنامه social media فرضي است و مسئول ارزیابی میزان سازگاری دو دوست است.
// Domain Service Example // Stateless, Behavior‐Only, Entity‐Orchestrating public class FriendOMeter { public AffinityyRating AssessAffinity(Friend firstFriend, Friend secondFriend) { var rating = new AffinityyRating(0); if (firstFriend.BookType == secondFriend.BookType) { rating = rating + new AffinityyRating(10); } if (firstFriend.MusicStyle == secondFriend.MusicStyle) { rating = rating + new AffinityyRating(15); } // You can put more rules here. return rating; } }
همانطور که در ابتدا گفته شد هیچ شناسه یا وضعیت مربوط به شناسایی در FriendOMeter وجود ندارد. این فقط شامل رفتاری در داخل AssessAffimity است که محاسبه AffinityRating دوستان را اجرا می کند. همانطور که دیدید، شامل حالت نیست و مسئول سازماندهی چندین موجودیت Friend است. محاسبه کاملاً بر اساس موجودیت ها است. و نشان مي دهد که چگونه سرویسهای دامنه سایر اشیاء دامنه را هماهنگ میکنند.
برخلاف Application Services که Data Transfer Objects يا همان Dto ها را دریافت و يا باز برمي گرداند، یک Domain Service اشیاء دامنه/domain objects (مانند entities يا value types) را دریافت يا
باز می گرداند.Application Serviceها بیشتر یک لایه روی Domain هستند که به یک برنامه اجازه میدهد با دامنه بدون نیاز به دانستن جزئیات کامل در مورد نحوه عملکرد آن تعامل داشته باشد.
یک Domain Service را می توان توسط Application Services و سایر Domain Services استفاده کرد، اما نه مستقیماً توسط لایه presentation.
در واقع Application Serviceها use case هاي برنامه (تعاملات کاربر در یک برنامه وب معمولی) را پیاده سازی می کند.Application Services توسط Presentation Layer یا Client Applications استفاده می شود.
خوب Domain Serviceها می توانند انواع مختلفی از وظایف را انجام دهند بياييد يک مثال ديگر را بررسي کنيم
در اين مثال Domain Service فرض کنید که ما یک سیستم (task management system) داريم و در حین تعیین تکلیف به یک فرد، business ruleها را داریم. آن را بررسي خواهيم کرد.
ما باید Domain Service را در مدل دامنه خود اعلام کنیم.از آنجا که ما نمی خواهیم به چیزی در خارج از مدل دامنه خود وابسته باشیم، ما نباید جزئیات پیاده سازی را در مدل دامنه قرار دهیم. یعنی تنها چیزی که ما در داخل پروژه دامنه خواهیم داشت، domain service interface است.(هر زمان که ساختاری بدون حالت stateless ارائه می کنيد باید آن را در یک شی دیگر تزریق کنيد، یک اينترفيس تعریف مي کنيد)
بنابراين ابتدا، یک اينترفيس برای سرویس تعریف می کنیم
این سرویس منطق تجاری کاملی را برای اختصاص taskها به یک شخص انجام می دهد.
که در قطعه کد زیر نشان داده شده است:
public interface ITaskManager : IDomainService { void AssignTaskToPerson(Task task, Person person); }
همانطور که می بینید، سرویس TaskManager با اشیاء دامنه کار می کند: Task و Person.
برخی از قراردادها هنگام نامگذاری domain serviceها وجود دارد. این می تواند TaskManager، TaskService یا TaskDomainService باشد...
پياده سازي سرويس
public class TaskManager : DomainService, ITaskManager { public const int MaxActiveTaskCountForAPerson = 3; private readonly ITaskRepository _taskRepository; public TaskManager(ITaskRepository taskRepository) { _taskRepository = taskRepository; } public void AssignTaskToPerson(Task task, Person person) { if (task.AssignedPersonId == person.Id) { return; } if (task.State != TaskState.Active) { throw new ApplicationException("Can not assign a task to a person when task is not active!"); } if (HasPersonMaximumAssignedTask(person)) { throw new UserFriendlyException(L("MaxPersonTaskLimitMessage", person.Name)); } task.AssignedPersonId = person.Id; } private bool HasPersonMaximumAssignedTask(Person person) { var assignedTaskCount = _taskRepository.Count(t => t.State == TaskState.Active && t.AssignedPersonId == person.Id); return assignedTaskCount >= MaxActiveTaskCountForAPerson; } }
ما در اینجا دو business rule داریم:
تعجب می کنید که چرا یک ApplicationException برای اولين بررسي و UserFriendlyException برای دومين بررسي throw Exception مي کنيم؟ این به هیچ وجه مربوط به Domain Service نیست. این فقط یک مثال است، کاملاً به شما بستگی دارد. رابط کاربری باید وضعیت یک Task را بررسی کند و نباید به ما اجازه دهد آن را به شخصی اختصاص دهیم. این یک خطا در سطح Application است.
استفاده از Domain Service از یک Application Service
اکنون، بیایید نحوه استفاده از TaskManager را از یک Application Service ببینیم:
public class TaskAppService : ApplicationService, ITaskAppService { private readonly IRepository<Task, long> _taskRepository; private readonly IRepository<Person> _personRepository; private readonly ITaskManager _taskManager; public TaskAppService(IRepository<Task, long> taskRepository, IRepository<Person> personRepository, ITaskManager taskManager) { _taskRepository = taskRepository; _personRepository = personRepository; _taskManager = taskManager; } public void AssignTaskToPerson(AssignTaskToPersonInput input) { var task = _taskRepository.Get(input.TaskId); var person = _personRepository.Get(input.PersonId); _taskManager.AssignTaskToPerson(task, person); } }
اينجا Task Application Service از یک DTO (ورودی) معین استفاده می کند، سپس از Repository برای بازیابی آن Task و شخص مرتبط استفاده می کند. در نهایت، آنها را به Task Manager (سرویس دامنه/Domain Service) منتقل می کند.
ممکن است تعجب کنید که چرا Application Service خود منطق موجود در Domain Service را اعمال
نمی کند.
به سادگی می توانیم بگوییم که این یک وظیفه Application Service نیست. از آنجایی که این یک use case نیست، در عوض، یک عملیات business است، ممکن است در نهایت از همان منطق دامنه «تخصیص وظیفه به کاربر» در یک use case متفاوت استفاده کنیم. ممکن است 2 رابط کاربری مختلف (یک برنامه تلفن همراه و یک برنامه وب) داشته باشیم که دامنه مشابهی را به اشتراک بگذارند.
اگر دامنه شما ساده است، فقط یک رابط کاربری خواهد داشت، و تعیین یک Task به یک شخص می تواند تنها در یک نقطه انجام شود، در این صورت می توانید از Domain Service صرفنظر کنید و منطق را در Application Service خود پیاده سازی کنید.
در نهايت Domain Services جزء بسیار مهمی از Domain Driven Design است، زیرا به شما امکان میدهد تا اشیاء دامنه را هماهنگ کنید و از Entityها و Value Objectها در برابر مسئولیت بیش از حد محافظت کنید.
با این حال بسیار مهم است که از استفاده بیش از حد از Domain Serviceها خودداری کنید. به نظر می رسد یک Domain Service یک راه حل ساده برای یک مشکل است، اما می توانید خیلی سریع در موقعیتی قرار بگیرید که منطق دامنه را از همان اشیایی که بیشتر به آن نیاز دارند ربوده اید.
بیشتر بخوانید : Implementing DDD - Clean Architecture
بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Core
https://zarinp.al/farshidazizi