實現領域驅動設計 - 使用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)

浙公網安備 33010602011771號