از Application Serviceها برای پیاده سازی use caseهای یک برنامه کاربردی استفاده می شود. آنها برای نمایش منطق دامنه در لایه presentation استفاده می شوند.
یک Application Service از لایه presentation(اختیاری) با یک DTO (Data Transfer Object) به عنوان پارامتر فراخوانی می شود. از اشیاء دامنه برای انجام برخی business logicهای خاص استفاده می کند و (به صورت اختیاری) یک DTO را به لایه presentation برمی گرداند. بنابراین، لایه presentation به طور کامل از لایه دامنه جدا می شود.
مثال :
Book Entity :
فرض کنید که یک موجودیت Book (در واقع یک aggregate root) دارید که به شکل زیر تعریف شده است:
public class Book : AggregateRoot<Guid> { public const int MaxNameLength = 128; public virtual string Name { get; protected set; } public virtual BookType Type { get; set; } public virtual float? Price { get; set; } protected Book() { } public Book(Guid id, [NotNull] string name, BookType type, float? price = 0) { Id = id; Name = CheckName(name); Type = type; Price = price; } public virtual void ChangeName([NotNull] string name) { Name = CheckName(name); } private static string CheckName(string name) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException( $"name can not be empty or white space!"); } if (name.Length > MaxNameLength) { throw new ArgumentException( $"name can not be longer than {MaxNameLength} chars!"); } return name; } }
IBookAppService Interface :
یک Application Service باید رابط IApplicationService را پیاده سازی کند. در واقع باید به معنای خوب است که برای هر Application Service یک اینترفیس ایجاد کنید:
public interface IBookAppService : IApplicationService { Task CreateAsync(CreateBookDto input); }
یک متد Create به عنوان مثال پیاده سازی خواهد شد. CreateBookDto به این صورت تعریف می شود:
public class CreateBookDto { [Required] [StringLength(Book.MaxNameLength)] public string Name { get; set; } public BookType Type { get; set; } public float? Price { get; set; } }
BookAppService (Implementation) :
public class BookAppService : IBookAppService { private readonly IRepository<Book, Guid> _bookRepository; public BookAppService(IRepository<Book, Guid> bookRepository) { _bookRepository = bookRepository; } public async Task CreateAsync(CreateBookDto input) { var book = new Book( Guid.NewGuid(), input.Name, input.Type, input.Price ); await _bookRepository.InsertAsync(book); } }
Data Transfer Objects :
توجه داشته باشید برنامه به جای موجودیت ها، DTOها را دریافت و برمی گرداند. قرار دادن موجودیت ها در معرض لایه presentation (یا remote client) مشکلات قابل توجهی دارد و پیشنهاد نمی شود.
Object to Object Mapping :
متد CreateAsync در بالا به صورت دستی یک موجودیت Book را از شیء داده شده CreateBookDto ایجاد می کند.با این حال، در بسیاری از موارد، استفاده از نگاشت خودکار شیء(auto object mapping) برای تنظیم propertyهای یک شی از یک شیء مشابه توصیه می شود(استفاده از کتابخانه AutoMapper).
بیشتر بخوانید : پیاده سازی object to object mapping با AutoMapper
بیایید متد دیگری برای دریافت کتاب ایجاد کنیم. ابتدا متدرا در رابط IBookAppService تعریف کنید:
public interface IBookAppService : IApplicationService { Task CreateAsync(CreateBookDto input); Task<BookDto> GetAsync(Guid id); //New method }
خوب BookDto یک کلاس DTO ساده است که به صورت زیر تعریف شده است:
public class BookDto { public Guid Id { get; set; } public string Name { get; set; } public BookType Type { get; set; } public float? Price { get; set; } }
خوب AutoMapper نیاز به ایجاد یک کلاس mapping profile دارد. مثال:
public class MyProfile : Profile
{ public MyProfile() { CreateMap<Book, BookDto>(); } }
در اینجا باید AutoMapper را ریجستر کنیم
برای پیکربندی و ریجستر نمودن Mapping در Asp.Net Core 6 :
فایل Program.cs :
builder.Services.AddAutoMapper(typeof(<MyProfile>))
سپس می توانید متد GetAsync را مطابق شکل زیر پیاده سازی کنید:
public async Task<BookDto> GetAsync(Guid id) { var book = await _bookRepository.GetAsync(id); return ObjectMapper.Map<Book, BookDto>(book); }
Validation :
شما می توانید از data annotation attributes استاندارد یا یک روش اعتبار سنجی سفارشی برای انجام اعتبار سنجی استفاده کنید.
Authorization :
شما می توانید از مجوزهای اعلامی Authorization برای متد های Application Service استفاده کنید.
CRUD Application Services:
اگر نیاز به ایجاد یک CRUD application service ساده دارید که دارای متدهای Create، Update، Delete و Get باشد، می توانید از کلاس پایه CrudAppService ارث ببرید.
مثال :
یک اینترفیس IBookAppService ایجاد کنید که از اینترفیس ICrudAppService ارث بری می کند.
public interface IBookAppService : ICrudAppService< //Defines CRUD methods BookDto, //Used to show books Guid, //Primary key of the book entity PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books CreateUpdateBookDto, //Used to create a new book CreateUpdateBookDto> //Used to update a book
خوب اینترفیس ICrudAppService دارای آرگومان های generic برای دریافت primary key موجودیت و انواع DTO برای عملیات CRUD است.ICrudAppService متد های زیر را اعلام می کند:
public interface ICrudAppService< TEntityDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput> : IApplicationService where TEntityDto : IEntityDto<TKey> { Task<TEntityDto> GetAsync(TKey id); Task<PagedResultDto<TEntityDto>> GetListAsync(TGetListInput input) Task<TEntityDto> CreateAsync(TCreateInput input); Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input); Task DeleteAsync(TKey id); }
کلاس های DTO استفاده شده در این مثال BookDto و CreateUpdateBookDto هستند:
public class BookDto : AuditedEntityDto<Guid> { public string Name { get; set; } public BookType Type { get; set; } public float Price { get; set; } } public class CreateUpdateBookDto { [Required] [StringLength(128)] public string Name { get; set; } [Required] public BookType Type { get; set; } = BookType.Undefined; [Required] public float Price { get; set; } }
و Profile class از کلاس های DTO
public class MyProfile : Profile { public MyProfile() { CreateMap<Book, BookDto>(); CreateMap<CreateUpdateBookDto, Book>(); } }
در این مثال CreateUpdateBookDto توسط عملیات Create و Update به اشتراک گذاشته میشود، اما میتوانید از کلاسهای DTO جداگانه نیز استفاده کنید.
و در نهایت، پیاده سازی BookAppService بسیار ساده است:
public class BookAppService : CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto, CreateUpdateBookDto>, IBookAppService { public BookAppService(IRepository<Book, Guid> repository) : base(repository) { } }
خوب CrudAppService تمام متدهای اعلام شده در رابط ICrudAppService را پیاده سازی می کند. سپس
می توانید متدهای سفارشی خود را اضافه کنید یا متدهای پایه را override و سفارشی کنید.
بیشتر بخوانید : Implementing DDD - Clean Architecture
بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Core