基于ABP落地領域驅動設計第四篇,在DDD架構中兩個最核心的層為:領域層和應用,本篇文章讨論領域服務和應用服務的最佳實踐和原則。
目錄
系列文章
領域服務
應用服務
學習幫助
基于ABP落地領域驅動設計-00.目錄和前言
基于ABP落地領域驅動設計-01.全景圖
基于ABP落地領域驅動設計-02.聚合和聚合根的最佳實踐和原則
基于ABP落地領域驅動設計-03.倉儲和規約最佳實踐和原則
基于ABP落地領域驅動設計-04.領域服務和應用服務的最佳實踐和原則
基于ABP落地領域驅動設計-05.實體建立和更新最佳實踐
基于ABP落地領域驅動設計-06.正确區分領域邏輯和應用邏輯
圍繞DDD和ABP Framework兩個核心技術,後面還會陸續釋出核心構件實作、綜合案例實作系列文章,敬請關注! ABP Framework 研習社(QQ群:726299208) ABP Framework 學習及實施DDD經驗分享;示例源碼、電子書共享,歡迎加入!
領域服務實作領域邏輯,它:
依賴于服務和倉儲。
需要多個聚合,以實作單個聚合無法處理的邏輯。
領域服務與領域對象一起使用,其方法可以擷取和傳回實體、值對象、原始類型等。然而,它并不擷取/傳回DTOs,DTOs屬于應用層。
示例:将問題配置設定給使用者
回想一下,我們之前是如何實作将問題配置設定給使用者的
現在,我們将邏輯遷移到領域服務中。首先,修改 Issue 類:
在聚合中移除 <code>AssignToAsync</code> 方法(因為需要在對應的領域服務中實作該方法。)
将 <code>AssignedUserId</code> 屬性設定器從私有改為内部<code>internal</code>,以允許從領域服務中設定它。
接下來,建立一個領域服務 <code>IssueManager</code> 定義方法 <code>AssignToAsync</code> 将指定 <code>Issue</code> 配置設定給指定使用者。
<code>IssueManager</code>在構造函數中注入需要的倉儲,用于查詢配置設定給使用者處于打開狀态的Issue。
建議使用<code>Manager</code>字尾命名來命名領域服務。
這種設計的唯一問題是:<code>Issue.AssignedUserId</code>現在是 <code>public</code> ,可以在任何外部類中設定。然而,它不應該是公共的,通路範圍應該是程式集内部<code>internal</code>,隻有在同一個程式集(<code>IssueTracking.Domain</code>)項目中才可以調用。
這個例子的解決方案就是如此,我們認為這很合理:
領域層開發者在使用 IssueManager 時,已經熟知領域規則。
應用層開發者強制使用 IssueManager,是以無法直接修改實體。
以上我們展示了将問題配置設定給使用者的兩種實作方式,兩種方式權衡之下,我們更加推薦當業務邏輯需要與外部服務協同工作時,建立領域服務。
如果沒有一個充分的理由,我們認為沒有必要去為領域服務建立接口,比如:為 <code>IssueManager</code> 建立 <code>IIssueManger</code> 接口。
應用服務是無狀态服務,實作應用程式用例。一個應用服務通常使用領域對象實作用例,擷取或傳回資料傳輸對象DTOs,被展示層調用。
應用服務通用原則:
實作特定用例的應用邏輯,不能在應用服務中實作領域邏輯(需要理清應用邏輯和領域邏輯二者的差別)。
應用服務方法不能傳回實體,因為這樣會打破領域層的封裝性,始終隻傳回DTO。
示例:配置設定問題給使用者
一個應用服務方法通常有三個步驟:
從資料庫擷取關聯的領域對象
使用領域對象(領域服務、實體等)執行業務邏輯
在資料庫中更新實體(如果已修改)
當時使用EF Core時,最後的 Update 更新操作并不是必須的,應為有 狀态變更跟蹤。但是建議顯式調用,适配其他資料庫提供程式。
示例中 <code>IssueAssignDto</code> 是一個簡單的 DTO 類:
專注 ABP Framework 學習及DDD實施經驗分享;示例源碼、電子書共享,歡迎加入!
記錄技術修行中的反思與感悟,以碼傳心,以軟制道,知行合一!