如何利用ABP框架实现领域驱动设计中的领域服务功能?

2026-04-18 03:071阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计735个文字,预计阅读时间需要3分钟。

如何利用ABP框架实现领域驱动设计中的领域服务功能?

领域服务+领域服务实现领域逻辑+依赖于服务和数据库。需要处理多个聚合,因为该逻辑不适合任何聚合。领域服务与领域对象协同工作。它们的方法可以获取并返回实体、值对象和原型的信息。

领域服务

领域服务实现领域逻辑

  • 依赖于服务和存储库。
  • 需要处理多个聚合,因为该逻辑不适合任何聚合。

领域服务与领域对象一起工作。它们的方法可以获取并返回实体、值对象、原始类型……但是,它们不获取/返回dto。dto是应用层的一部分

示例:分配问题给用户

记住问题分配是如何在问题实体中实现的

public class Issue : AggregateRoot<Guid> { public 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:ConcurrentOpenIssueLimit"); } AssignedUserId = user.Id; } public void CleanAssignment() { AssignedUserId = null; } }

在这里,我们将把这个逻辑转移到领域服务中。
首先,修改Issue类:

public class Issue : AggregateRoot<Guid> { public Guid? AssignedUserId { get; internal set; } }

  • 删除了与分配问题相关的方法。
  • 将 AssignedUserId 属性的setter从私有改为内部,以允许从领域服务设置它

下一步是创建一个名为 IssueManager 的领域服务,该服务具有AssignToAsync 来将给定问题分配给给定用户

public class IssueManager : DomainService { private readonly IRepository<Issue, Guid> _issueReposiroty; public IssueManager(IRepository<Issue, Guid> issueReposiroty) { _issueReposiroty = issueReposiroty; } public async Task AssignToAsync(Issue issue, AppUser user) { var openIssueCount = await _issueRepository.CountAsync(i => i.AssignedUserId == user.Id && !i.IsClosed); if(openIssueCount >= 3) { throw new BusinessException("IssueTracking:ConcurrentOpenIssueLimit"); } issue.AssignedUserId = user.Id; } }

IssueManager 可以注入任何依赖的服务,并用于查询用户的分配问题数量。
我们更喜欢并建议为领域服务使用 Manager 后缀。

这个设计的唯一问题就是 Issue.AssignedUserId 现在在类外开放设置了。然而,它不是公开的。它是内部的,并且只能在同一个程序集中(IssueTracking)进行更改。此示例解决方案的领域项目。我们认为这是合理的

  • 领域层开发人员已经知道领域规则,他们使用 IssueManager
  • 应用层开发人员已经被强制使用IssueManager,他们不直接设置它。

虽然两种方法之间存在权衡,但当业务逻辑需要使用外部服务时,我们更喜欢创建域服务

如果你没有一个好的理由,我们认为没有必要为领域服务创建接口(比如为IssueManager创建IIssueManager)

如何利用ABP框架实现领域驱动设计中的领域服务功能?

本文共计735个文字,预计阅读时间需要3分钟。

如何利用ABP框架实现领域驱动设计中的领域服务功能?

领域服务+领域服务实现领域逻辑+依赖于服务和数据库。需要处理多个聚合,因为该逻辑不适合任何聚合。领域服务与领域对象协同工作。它们的方法可以获取并返回实体、值对象和原型的信息。

领域服务

领域服务实现领域逻辑

  • 依赖于服务和存储库。
  • 需要处理多个聚合,因为该逻辑不适合任何聚合。

领域服务与领域对象一起工作。它们的方法可以获取并返回实体、值对象、原始类型……但是,它们不获取/返回dto。dto是应用层的一部分

示例:分配问题给用户

记住问题分配是如何在问题实体中实现的

public class Issue : AggregateRoot<Guid> { public 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:ConcurrentOpenIssueLimit"); } AssignedUserId = user.Id; } public void CleanAssignment() { AssignedUserId = null; } }

在这里,我们将把这个逻辑转移到领域服务中。
首先,修改Issue类:

public class Issue : AggregateRoot<Guid> { public Guid? AssignedUserId { get; internal set; } }

  • 删除了与分配问题相关的方法。
  • 将 AssignedUserId 属性的setter从私有改为内部,以允许从领域服务设置它

下一步是创建一个名为 IssueManager 的领域服务,该服务具有AssignToAsync 来将给定问题分配给给定用户

public class IssueManager : DomainService { private readonly IRepository<Issue, Guid> _issueReposiroty; public IssueManager(IRepository<Issue, Guid> issueReposiroty) { _issueReposiroty = issueReposiroty; } public async Task AssignToAsync(Issue issue, AppUser user) { var openIssueCount = await _issueRepository.CountAsync(i => i.AssignedUserId == user.Id && !i.IsClosed); if(openIssueCount >= 3) { throw new BusinessException("IssueTracking:ConcurrentOpenIssueLimit"); } issue.AssignedUserId = user.Id; } }

IssueManager 可以注入任何依赖的服务,并用于查询用户的分配问题数量。
我们更喜欢并建议为领域服务使用 Manager 后缀。

这个设计的唯一问题就是 Issue.AssignedUserId 现在在类外开放设置了。然而,它不是公开的。它是内部的,并且只能在同一个程序集中(IssueTracking)进行更改。此示例解决方案的领域项目。我们认为这是合理的

  • 领域层开发人员已经知道领域规则,他们使用 IssueManager
  • 应用层开发人员已经被强制使用IssueManager,他们不直接设置它。

虽然两种方法之间存在权衡,但当业务逻辑需要使用外部服务时,我们更喜欢创建域服务

如果你没有一个好的理由,我们认为没有必要为领域服务创建接口(比如为IssueManager创建IIssueManager)

如何利用ABP框架实现领域驱动设计中的领域服务功能?