甚么是应用领域服务项目呢?应用领域服务项目是应用领域第一类这类的服务项目,一般来说是透过数个裂解以同时实现一般而言裂解难以处置的方式论。
一.应用领域服务项目课堂教学
接下去将裂解根Issue中的AssignToAsync()方式[将难题分口粮使用者],剥离到应用领域服务项目之中。如下表所示:
// ABP之中的应用领域服务项目类一般来说都是以Manager开头的public class IssueManager : DomainService{ private readonly IRepository issueRepository;//在缺省中转化成须要的物流配送 public IssueManager(IRepository issueRepository){ issue口粮该使用者的,因此没停用的Issue的数目 var openIssueCount = await issueRepository.CountAsync(i => i.AssignedUserId == user.id &&!i.IsClosed);//假如少于3个,所以放出极度if (openIssueCount >3){ throw new BusinessException(“IssueTracking:ConcurrentOpenIssueLimit”);} issue.AssignedUserId = user.Id;}}
须要表明的是一般来说不须要为应用领域服务项目IssueManager在建立两个USBIIssueManager。
二.数据服务项目课堂教学
数据服务项目的输入和输入一般来说都是DTO,当中的症结是界定应用领域方式论和应用方式论,即甚么样服务项目放到应用领域层同时实现,甚么样服务项目放到网络层来同时实现。
namespace IssueTracking.Issues{ public class IssueAppService :ApplicationService.IIssueAppService { private readonly IssueManager issueManager; private readonly IRepository issueRepository; private readonly IRepository userRepository; public IssueAppService( IssueManager issueManager, IRepository issueRepository, IRepository userRepository ){ issueManager=issueManager; issueRepository=issueRepository; userRepository=userRepository;} [Authorize] public async Task AssignAsync(IssueAssignDto input){ var issue=await issueRepository.GetAsync(input.IssueId); var user=await userRepository.GetAsync(inpu.UserId); await issueManager.AssignToAsync(issue,user); await issueRepository.UpdateAsync(issue);} }}
在上述代码中,为什么最后执行issueRepository.UpdateAsync(issue)呢?当中有2层含义,第1层是Issue透过issueManager.AssignToAsync(issue,user)发生了变化,须要进行更新操作(从下图可知Issue裂解根中包含AssignedUserId字段);第2层是EF Core中有状态变更跟踪,Update并不是必须的,但是还是建议显式调用Update,用来适配其它的数据库提供程序。
三.数据传输第一类DTO课堂教学
DTO的本质是在网络层和展示层传递状态数据,一般来说网络层的输入和输入都是DTO,这样做的最大好处是不暴露实体的结构设计。
1.输入DTO课堂教学
(1)不要重用输入DTO不使用的属性不要定义在输入DTO中;不要重用输入DTO有2种方式:一种方式是为每个数据服务项目方式定义特定的输入DTO,另一种方式是不要使用DTO继承。下面是错误的输入DTO课堂教学,理由详见注释:
public interface IUserAppService : IApplicationService{ Task CreateAsync(UserDto input);//Id在该方式中没用到 Task UpdateAsync(UserDto input);// Password在该方式中没用到 Task ChangePasswordAsync(UserDto input);// CreationTime在该方式中没用到}public class UserDto{ public Guid Id { get; set;} public string UserName { get; set;} public string Email { get; set;} public string Password { get; set;} public DateTime CreateTime { get; set;}}
下面是正确的输入DTO课堂教学:
public interface IUserAppService : IApplicationService{ Task CreateAsync(UserCreationDto input); Task UpdateAsync(UserUpdateDto input); Task ChangePasswordAsync(UserChangePasswordDto input);}public class UserCreationDto{ public string UserName { get; set;} public string Email { get; set;} public string Password { get; set;}}public class UserUpdateDto{ public Guid Id { get; set;} public string UserName { get; set;} public string Email { get; set;}}public class UserChangePasswordDto{ public Guid Id { get; set;} public string Password { get; set;}}
(2)输入DTO中的验证方式论主要是在DTO内部透过数据注解特性、FluentValidation,或者同时实现IValidatableObjectUSB等方式来执行简单的验证。须要注意的是不要在DTO中执行应用领域验证,比如检测使用者名是否唯一的验证等。下面在输入DTO中使用数据注解特性:
namespace IssueTracking.Users{ public class UserCreationDto {[Required][StringLength(UserConsts.MaxUserNameLength)] public string UserName {get;set;}[Required][EmailAddress][StringLength(UserConsts.MaxEmailLength)] public string Email{get;set;}[Required][StringLength(UserConsts.MaxEmailLength,MinimumLength=UserConsts.MinPasswordLength)] public string Password{get;set;}}}
ABP会自动验证输入DTO中的注解,假如验证失败,所以放出AbpValidationException极度,因此返回400状态码。个人建议使用FluentValidation方式进行验证,而不是声明式的数据注解,这样做的优点是将验证规则和DTO类彻底分离开。
2.输入DTO课堂教学
输入DTO最佳课堂教学:主要是尽可能的复用输入DTO,但是切记不能把输入DTO作为输入DTO;输入DTO可以包含更多的属性;Create和Update方式返回DTO。下面是错误的输入DTO课堂教学:
public interface IUserAppService:IApplicationService{ UserDto Get(Guid id); List GetUserNameAndEmail(Guid id); List GetRoles(Guid id); List GetList(); UserCreateResultDto Create(UserCreationDto input); UserUpdateResultDto Update(UserUpdateDto input);}
下面是正确的输入DTO课堂教学:
public interface IUserAppService:IApplicationService{ UserDto Get(Guid id); List GetList(); UserDto Create(UserCreationDto input); UserDto Update(UserUpdateDto input);}public class UserDto{ public Guid Id{get;set;} public string UserName{get;set;} public string Email{get;set;} public DateTiem CreationTime{get;set;} public List Roles{get;set;}}
表明:删除GetUserNameAndEmail()和GetRoles()方式,因为它们与Get()方式重复了,即它们的功能都可以透过Get()方式来同时实现。
3.第一类映射工具
为甚么须要第一类映射工具呢?由于实体和DTO具有相同或者相似的属性,假如手工处置实体和DTO间的转换,所以效率是非常低的,因此须要第一类映射工具高效的完成实体和DTO间的转换。在ABP中使用的第一类映射框架是AutoMapper,官方的建议是:仅对实体到输入DTO做自动第一类映射,不建议输入DTO到实体做自动第一类映射。因为DTO是实体的部分或者全部字段,自己推测前者是比较确定的,而由于复杂的业务规则让后者的映射充满了不确定性。具体为甚么不使用输入DTO到实体做自动第一类映射的原因参考[1]。自动第一类映射在数据服务项目层中同时实现,该类须要继承自Profile类:虽然官方不建议输入DTO到实体做自动第一类映射,但是在一般来说的课堂教学中还是较多使用CreateOrUpdateXXXDto到实体XXX的自动第一类映射:关于FluentValidation和AutoMapper这2个库就不单独在这里展开讲了,后面单独文章进行讲解操作和原理。
参考文献:[1]如前所述ABP Framework同时实现应用领域驱动设计:https://url39.ctfile.com/f/2501739-616007877-f3e258?p=2096(访问密码:2096)[2]FluentValidation官方文档:https://docs.fluentvalidation.net/en/latest/[3]FluentValidation GitHub:https://github.com/FluentValidation/FluentValidation/blob/main/docs/index.rst[4]AutoMapper官方文档:http://automapper.org/[5]AutoMapper GitHub:https://github.com/AutoMapper/AutoMapper