1

基于ABP实现DDD--领域逻辑和应用逻辑 - 阿升1990

 1 year ago
source link: https://www.cnblogs.com/shengshengwang/p/16516035.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

  本文主要介绍了多应用层的问题,包括原因和实现。通过理解介绍了如何区分领域逻辑和应用逻辑,哪些是正确的实践,哪些是不推荐的或者错误的实践。

一.多应用层的问题

1.多应用层介绍

  不知道你们是否会遇到一种情况,通过ABP构建了一个后端的API项目,刚开始是为Web端项目(比如,Vue)提供后端接口服务的,随着项目的发展和业务的复杂,增加了移动端的App,或者公众号、小程序等,这样不仅要为Web端提供API服务,而且还需要为移动端的App,或者公众号、小程序等提供API服务。这个场景就是多应用层的问题。也就是说你现在需要构建一个有多个应用程序的系统了:

d8dbe3a6-700a-4752-b2af-8f82677b9703.png
  • Web应用程序:比如使用的ASP.NET Core MVC技术,主要用来给用户展示产品,游客模式是可以查看产品的,并不需要登录和验证。
  • 后端管理应用程序:比如前端是Vue,后端的ASP.NET Core API,主要用来对产品进行增删改等操作。
  • 移动应用程序:比如前端是uni-app,后端是ASP.NET Core API,通过REST接口和后端通信。

2.多应用层例子

  由于业务的复杂性,每个应用系统都有自己的不同应用服务方法,不同的输入和输出DTO,不用的认证和授权规则等,所以如果把所有的业务逻辑都融入到一个应用系统中,就会让系统更难开发、维护和测试,并导致潜在的Bug风险。因此,为每个应用程序创建单独的应用层,并且它们都是用单个领域层来共享核心领域逻辑。为了更加具体的说明,为每种应用程序类型创建不同的.csproj项目:

  • 后端管理应用程序:IssueTracker.Admin.Application和IssueTracker.Admin.Application.Contracts项目
  • Web应用程序:IssueTracker.Public.Application和IssueTracker.Public.Application.Contracts项目
  • 移动应用程序:IssueTracker.Mobile.Application和IssueTracker.Mobile.Application.Contracts项目

二.如何区分领域逻辑和应用逻辑

通常,DDD中的业务逻辑包括领域逻辑和应用逻辑。领域逻辑由系统的核心领域规则组成,而应用程序逻辑实现特定于应用程序的用例。话虽这样说,但是并不容易区分什么是领域逻辑和应用逻辑。

1.在领域服务层中创建组织Organization

下面通过在领域服务中创建Organization这个例子,来尽可能简要说明:

public class OrganizationManager:DomainService
{
    private readonly IRepository<Organization> _organizationRepository; //Organization的仓储
    private readonly ICurrentUser _currentUser; //当前用户
    private readonly IAuthorizationService _authorizationService; //Authorization的服务
    private readonly IEmailSender _emailSender; //邮件发送服务
    
    // 公共构造函数,依赖注入
    public OrganizationManager(IRepository<Organization> organizationRepository, ICurrentUser currentUser, IAuthorizationService authorizationService, IEmailSender emailSender)
    {
        _organizationRepository=organizationRepository;
        _currentUser=currentUser;
        _authorizationService=authorizationService;
        _emailSender=emailSender;
    }
    
    // 创建一个新的组织
    public async Task<Organization> CreateAsync(string name)
    {
        // 如果组织存在同名,那么抛出异常[正确]
        if(await _organizationRepository.AnyAsync(x=>x.Name==name))
        {
            throw new BusinessException("IssueTracking:DuplicateOrganizationName");
        }
        
        // 检查是否拥有创建的权限[错误]
        await _authorizationService.CheckAsync("OrganizationCreationPermission");
        
        // 记录⽇志[错误]
        Logger.LogDebug($"Creating organization {name} by {_currentUser.UserName}");
        
        // 创建一个新的组织
        var organization = new Organization();
        
        // 发送邮件进行提醒[错误]
        await _emailSender.SendAsync("[email protected]", "新组织", "新组织名称:"+name);
        
        // 返回一个组织实例
        return organization;
    }
}
  • 领域服务不做权限验证,权限验证放在应用层来做
  • 用户概念是应用层或展示层的相关概念,记录日志不应该包含当前用户的用户名
  • 创建一个新的组织,并发送邮件,这个业务逻辑也应该放在应用层

2.在应用层中使用领域服务创建组织Organization

public class OrganizationAppService:ApplicationService
{
    private readonly OrganizationManager _organizationManager; //组织的领域服务
    private readonly IPaymentService _paymentService; //支付服务
    private readonly IEmailSender _emailSender; //邮件服务
    
    // 公共构造函数,依赖注入
    public OrganizaitonAppService(OrganizationManager organizationManager, IPaymentService paymentService, IEmailSender emailSender)
    {
        _organizationManager=organizationManager;
        _paymentService=paymentService;
        _emailSender=emailSender;
    }
    
    // 创建组织
    [UnitOfWork][正确] //工作单元,用于提交事务
    [Authorize("OrganizationCreationPermission")][正确]
    public async Task<Organization> CreateAsync(CreateOrganizationDto input)
    {
        // ⽀付组织的费⽤[正确]
        await _paymentService.ChargeAsync(CurrentUser.Id, GetOrganizationPrice());
        
        // 通过领域服务,创建一个新的组织实例
        var organization = await _organizationManager.CreateAsync(input.Name);
        
        // 保存和更新组织到数据库中[正确]
        await _organizationManager.InsertAsync(organization);
        
        // 发送提醒邮件[正确]
        await _emailSender.SendAsync("[email protected]", "新组织", "新组织名称:"+name);
        
        //返回实例[错误]
        return organization;
    }
    private double GetOrganizationPrice()
    {
        return 42;//Gets form somewhere...
    }
}

应用服务层的输入和输出参数都是DTO,不能返回实体。至于为什么不将支付放在领域服务中,只能说业务重要也不一定放在领域服务中,详细原因说明参考[1]。

参考文献:
[1]基于ABP Framework实现领域驱动设计:https://url39.ctfile.com/f/2501739-616007877-f3e258?p=2096 (访问密码: 2096)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK