网站架构招聘,网络推广网站制作,珠海网站建设推广公司,企业信息管理系统er图围绕DDD和ABP Framework两个核心技术#xff0c;后面还会陆续发布核心构件实现、综合案例实现系列文章#xff0c;敬请关注#xff01; ABP Framework 研习社#xff08;QQ群#xff1a;726299208#xff09; ABP Framework 学习及实施DDD经验分享#xff1b;示例源码、… 围绕DDD和ABP Framework两个核心技术后面还会陆续发布核心构件实现、综合案例实现系列文章敬请关注 ABP Framework 研习社QQ群726299208 ABP Framework 学习及实施DDD经验分享示例源码、电子书共享欢迎加入领域服务领域服务实现领域逻辑它•依赖于服务和仓储。•需要多个聚合以实现单个聚合无法处理的逻辑。领域服务与领域对象一起使用其方法可以获取和返回实体、值对象、原始类型等。然而它并不获取/返回DTOsDTOs属于应用层。示例将问题分配给用户回想一下我们之前是如何实现将问题分配给用户的public class Issue:AggregateRootGuid
{//..//问题关联的用户IDpublic Guid? AssignedUserId{get;private set;}//分配方法public async Task AssignToAsync(AppUser user,IUserIssueService userIssueService){var openIssueCount await userIssueService.GetOpenIssueCountAsync(user.Id);if(openIssueCount 3 ){throw new BusinessException(IssueTracking:CanNotOpenLockedIssue);}AssignedUserIduser.Id;}public void CleanAssignment(){AssignedUserIdnull;}
}
现在我们将逻辑迁移到领域服务中。首先修改 Issue 类public class Issue:AggregateRootGuid
{//...public Guid? AssignedUserId{get;internal set;}
}
•在聚合中移除 AssignToAsync 方法因为需要在对应的领域服务中实现该方法。•将 AssignedUserId 属性设置器从私有改为内部internal以允许从领域服务中设置它。接下来创建一个领域服务 IssueManager 定义方法 AssignToAsync 将指定 Issue 分配给指定用户。public class IssueManager:DomainService
{private readonly IRepositoryIssue,Guid _issueRepository;public IssueManager(IRepositoryIssue,Guid issueRepository){_issueRepositoryissueRepository;}public async Task AssignToAsync(Issue issue,AppUser user){//获取关联用户处于打开状态问题的数量var openIssueCountawait _issueRepository.CountAsync(ii.AssingedUserIduser.Id !i.IsClosed);//超过3个则抛出异常if(openIssueCount3){throw new BusinessException(IssueTracking:ConcurrentOpenIssueLimit);}issue.AssignedUserIduser.Id;}
}
IssueManager在构造函数中注入需要的仓储用于查询分配给用户处于打开状态的Issue。建议使用Manager后缀命名来命名领域服务。这种设计的唯一问题是Issue.AssignedUserId现在是 public 可以在任何外部类中设置。然而它不应该是公共的访问范围应该是程序集内部internal只有在同一个程序集IssueTracking.Domain项目中才可以调用。这个例子的解决方案就是如此我们认为这很合理•领域层开发者在使用 IssueManager 时已经熟知领域规则。•应用层开发者强制使用 IssueManager因此无法直接修改实体。以上我们展示了将问题分配给用户的两种实现方式两种方式权衡之下我们更加推荐当业务逻辑需要与外部服务协同工作时创建领域服务。如果没有一个充分的理由我们认为没有必要去为领域服务创建接口比如为 IssueManager 创建 IIssueManger 接口。应用服务应用服务是无状态服务实现应用程序用例。一个应用服务通常使用领域对象实现用例获取或返回数据传输对象DTOs被展示层调用。应用服务通用原则•实现特定用例的应用逻辑不能在应用服务中实现领域逻辑需要理清应用逻辑和领域逻辑二者的区别。•应用服务方法不能返回实体因为这样会打破领域层的封装性始终只返回DTO。示例分配问题给用户using System;
using System.Threading.Tasks;
using IssueTracking.Users;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;namespace IssueTracking.Issues
{public class IssueAppService :ApplicationService.IIssueAppService{private readonly IssueManager _issueManager;private readonly IRepositoryIssue,Guid _issueRepository;private readonly IRepositoryAppUser,Guid _userRepository;public IssueAppService(IssueManager issueManager,IRepositoryIssue,Guid issueRepository,IRepositoryAppUser,Guid userRepository){_issueManagerissueManager;_issueRepositoryissueRepository;_userRepositoryuserRepository;}[Authorize]public async Task AssignAsync(IssueAssignDto input){var issueawait _issueRepository.GetAsync(input.IssueId);var userawait _userRepository.GetAsync(inpu.UserId);await _issueManager.AssignToAsync(issue,user);await _issueRepository.UpdateAsync(issue);//没有对issue做任何修改为什么要更新在IssueManager中进行了状态修改。}}
}
一个应用服务方法通常有三个步骤•从数据库获取关联的领域对象•使用领域对象领域服务、实体等执行业务逻辑•在数据库中更新实体如果已修改当时使用EF Core时最后的 Update 更新操作并不是必须的应为有 状态变更跟踪。但是建议显式调用适配其他数据库提供程序。示例中 IssueAssignDto 是一个简单的 DTO 类using System;
namespace IssueTracking.Issues
{public class IssueAssignDto{public Guid IssueId{get;set;}public Guid UserId{get;set;}}
}
学习帮助围绕DDD和ABP Framework两个核心技术后面还会陆续发布核心构件实现、综合案例实现系列文章敬请关注ABP Framework 研习社QQ群726299208 专注 ABP Framework 学习及DDD实施经验分享示例源码、电子书共享欢迎加入