项目

领域服务

领域驱动设计(DDD)解决方案中,核心业务逻辑通常通过聚合( 实体 )和领域服务来实现。在以下场景中尤其需要创建领域服务:

  • 需要实现依赖某些服务(如仓储或其他外部服务)的核心领域逻辑时
  • 需要实现的逻辑涉及多个聚合/实体,无法完美归属于任何一个聚合时

ABP领域服务基础设施

领域服务是简单的无状态类。虽然不需要从任何服务或接口继承,但ABP提供了有用的基类和约定。

DomainService 与 IDomainService

领域服务可以从 DomainService 基类派生,或直接实现 IDomainService 接口。

示例:创建继承自 DomainService 基类的领域服务

using Volo.Abp.Domain.Services;

namespace MyProject.Issues
{
    public class IssueManager : DomainService
    {
        
    }
}

这样做的好处:

  • ABP会自动以Transient生命周期将类注册到依赖注入系统
  • 可直接使用基类提供的常用服务属性,无需手动注入(例如 ILoggerIGuidGenerator

建议领域服务命名采用 ManagerService 后缀。如上例所示,我们通常使用 Manager 后缀

示例:实现将问题分配给用户的领域逻辑

public class IssueManager : DomainService
{
    private readonly IRepository<Issue, Guid> _issueRepository;

    public IssueManager(IRepository<Issue, Guid> issueRepository)
    {
        _issueRepository = issueRepository;
    }
    
    public async Task AssignAsync(Issue issue, AppUser user)
    {
        var currentIssueCount = await _issueRepository
            .CountAsync(i => i.AssignedUserId == user.Id);
        
        //实现核心业务验证
        if (currentIssueCount >= 3)
        {
            throw new IssueAssignmentException(user.UserName);
        }

        issue.AssignedUserId = user.Id;
    }    
}

Issue是如下定义的 聚合根

public class Issue : AggregateRoot<Guid>
{
    public Guid? AssignedUserId { get; internal set; }
    
    //...
}
  • 将setter设为internal可确保上层无法直接设置,强制始终使用IssueManager来将Issue分配给User

使用领域服务

领域服务通常在 应用服务 中使用

示例:使用IssueManager将问题分配给用户

using System;
using System.Threading.Tasks;
using MyProject.Users;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;

namespace MyProject.Issues
{
    public class IssueAppService : ApplicationService, IIssueAppService
    {
        private readonly IssueManager _issueManager;
        private readonly IRepository<AppUser, Guid> _userRepository;
        private readonly IRepository<Issue, Guid> _issueRepository;

        public IssueAppService(
            IssueManager issueManager,
            IRepository<AppUser, Guid> userRepository,
            IRepository<Issue, Guid> issueRepository)
        {
            _issueManager = issueManager;
            _userRepository = userRepository;
            _issueRepository = issueRepository;
        }

        public async Task AssignAsync(Guid id, Guid userId)
        {
            var issue = await _issueRepository.GetAsync(id);
            var user = await _userRepository.GetAsync(userId);

            await _issueManager.AssignAsync(issue, user);
            await _issueRepository.UpdateAsync(issue);
        }
    }
}

由于IssueAppService处于应用层,它不能直接将问题分配给用户,因此使用IssueManager来处理

应用服务 vs 领域服务

虽然 应用服务 和领域服务都实现业务规则,但存在根本性的逻辑和形式差异:

  • 应用服务实现应用程序的用例(典型Web应用中的用户交互),而领域服务实现核心的、与用例无关的领域逻辑
  • 应用服务获取/返回 数据传输对象 ,领域服务方法通常获取和返回领域对象实体值对象
  • 领域服务通常被应用服务或其他领域服务使用,而应用服务被表示层或客户端应用程序使用

生命周期

领域服务的生命周期为 Transient,它们会自动注册到依赖注入系统

另请参阅

在本文档中