日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于ABP落地领域驱动设计-04.领域服务和应用服务的最佳实践和原则

發布時間:2023/12/4 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于ABP落地领域驱动设计-04.领域服务和应用服务的最佳实践和原则 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

圍繞DDDABP Framework兩個核心技術,后面還會陸續發布核心構件實現綜合案例實現系列文章,敬請關注!?ABP Framework 研習社(QQ群:726299208)?ABP Framework 學習及實施DDD經驗分享;示例源碼、電子書共享,歡迎加入!

領域服務

領域服務實現領域邏輯,它:

?依賴于服務倉儲。?需要多個聚合,以實現單個聚合無法處理的邏輯。

領域服務與領域對象一起使用,其方法可以獲取和返回實體值對象、原始類型等。然而,它并不獲取/返回DTOs,DTOs屬于應用層。

示例:將問題分配給用戶

回想一下,我們之前是如何實現將問題分配給用戶的

public class Issue:AggregateRoot<Guid> {//..//問題關聯的用戶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");}AssignedUserId=user.Id;}public void CleanAssignment(){AssignedUserId=null;} }

現在,我們將邏輯遷移到領域服務中。首先,修改 Issue 類:

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

?在聚合中移除?AssignToAsync?方法(因為需要在對應的領域服務中實現該方法。)?將?AssignedUserId?屬性設置器從私有改為內部internal,以允許從領域服務中設置它。

接下來,創建一個領域服務?IssueManager?定義方法?AssignToAsync?將指定?Issue?分配給指定用戶。

public class IssueManager:DomainService {private readonly IRepository<Issue,Guid> _issueRepository;public IssueManager(IRepository<Issue,Guid> issueRepository){_issueRepository=issueRepository;}public async Task AssignToAsync(Issue issue,AppUser user){//獲取關聯用戶處于打開狀態問題的數量var openIssueCount=await _issueRepository.CountAsync(i=>i.AssingedUserId==user.Id && !i.IsClosed);//超過3個,則拋出異常if(openIssueCount>=3){throw new BusinessException("IssueTracking:ConcurrentOpenIssueLimit");}issue.AssignedUserId=user.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 IRepository<Issue,Guid> _issueRepository;private readonly IRepository<AppUser,Guid> _userRepository;public IssueAppService(IssueManager issueManager,IRepository<Issue,Guid> issueRepository,IRepository<AppUser,Guid> userRepository){_issueManager=issueManager;_issueRepository=issueRepository;_userRepository=userRepository;}[Authorize]public async Task AssignAsync(IssueAssignDto input){var issue=await _issueRepository.GetAsync(input.IssueId);var user=await _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;}} }

學習幫助

圍繞DDDABP Framework兩個核心技術,后面還會陸續發布核心構件實現綜合案例實現系列文章,敬請關注!

ABP Framework 研習社(QQ群:726299208)?專注 ABP Framework 學習及DDD實施經驗分享;示例源碼、電子書共享,歡迎加入!

總結

以上是生活随笔為你收集整理的基于ABP落地领域驱动设计-04.领域服务和应用服务的最佳实践和原则的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。