介紹
本章節對 ABP 框架進行一個簡單的介紹,摘自ABP官方,後面會在使用過程中對各個知識點進行細緻的講解。
領域驅動設計
領域驅動設計(簡稱:DDD)是一種針對複雜需求的軟體開發方法。將軟體實現與不斷髮展的模型聯絡起來,專注於核心領域邏輯,而不是基礎設施細節。DDD適用於複雜領域和大規模應用,而不是簡單的CRUD應用。它有助於建立一個靈活、模組化和可維護的程式碼庫。
一個基於領域驅動的解決方案有四個基本層:
領域層:實現領域(或系統)中的用例獨立的核心業務邏輯。
應用層:基於領域的應用程式用例,應用程式用例可以看作是使用者介面上的使用者互動。
展示層:包含應用程式UI元素(頁面、元件等)。
基礎層:支援層,通過對第三方類庫的呼叫或系統的抽象和整合來實現對其他層的支援。
核心構件
DDD主要關注領域層和應用層,展示層和基礎層被看作是細節,業務層不應該依賴於它們,但這並不意味著展示層和基礎層不重要,它們也非常重要。展示層中的UI框架和基礎層中的資料提供程式有他們自己的實現規則和最佳實踐,需要了解和應用。然而,這些並不在DDD的主題中,我們重點來看領域層和應用層的基本構件。
領域層構件
實體(Entity):一個實體是一個物件,該物件包含自己的屬性和方法,屬性用於儲存資料和描述狀態;方法結合屬性實現業務邏輯。一個實體使用唯一標識(ID)來表示,兩個實體物件ID不同則是為不同的實體。
值物件(Value Object):值物件是另一種型別的領域物件,該物件由其屬性而不是唯一ID來標識。意思是說,只有全部屬性相同才會被認為是同一個物件。值物件通常被實現為不可變的,而且大多比實體簡單得多。
聚合和聚合根:聚合根是一個特定型別的實體,具有額外的職責。聚合是以聚合根為中心繫結在一起的一組物件,物件包括實體和值物件。
倉儲(介面):倉儲是一個類似集合的介面,被領域層和應用層用來訪問資料持久化系統(資料庫)。它將資料庫的複雜性從業務程式碼中隱藏起來。領域層包含倉儲介面。
領域服務:領域服務是無狀態服務,實現核心領域業務規則。用於實現依賴於多個聚合(實體)或外部服務的領域邏輯。
規約:用於為實體和其他業務物件定義可命名的、可重用的和可組合的過濾器。
領域事件:領域事件是一種低耦合的通知方式,當一個特定的領域事件發生時,會通知其他服務。
應用層構件
應用服務:應用服務是無狀態服務,實現應用程式用例。一個應用服務通常獲取和返回資料傳輸物件(DTOs),用於展示層。呼叫領域物件來實現用例。一個用例通常被認為是一個工作單元。
資料傳輸物件(DTO):DTO是簡單物件,不包含任何業務邏輯,只用於在應用層和展示層傳遞資料。
工作單元:一個工作單元是一個原子工作。在工作單元中的所有操作統一提交,要麼全部成功,失敗則全部回滾。
ABP專案分層解析
領域層
領域層拆分為兩個專案:
Bcvp.Blog.Core.Domain:領域層,該專案包含所有領域層構件,比如:實體、值物件、領域服務、規約、倉儲介面等。
Bcvp.Blog.Core.Domain.Shared:領域共享層,包含屬於領域層,但是與其他層共享的型別。舉個例子:定義的常量和列舉,既在領域物件中使用,也要在其他層中使用,放在該專案中。
應用層
應用層拆分為兩個專案:
Bcvp.Blog.Core.Application.Contracts:應用契約層,包含應用服務介面和資料傳輸物件(用於介面),該專案被應用程式客戶端引用,比如:WEB專案、API客戶端專案。
Bcvp.Blog.Core.Application:應用層,實現在 Contracts 專案中定義的介面。
展示層
Bcvp.Blog.Core.HttpApi.Host 專案作為一個獨立的端點提供 HTTP API 服務,供客戶端呼叫。
遠端服務層
Bcvp.Blog.Core.HttpApi:遠端服務層,該專案用於定義 HTTP APIs,通常包含 MVC Controller 及相關的模型。
Bcvp.Blog.Core.HttpApi.Client:遠端服務代理層,客戶端應用程式引用該專案,將直接通過依賴注入使用遠端應用服務,該專案基於ABP Framework動態C#客戶端API代理系統實現。在C#專案中需要呼叫HTTP APIs時,會非常有用。
基礎層
實現DDD時,可以使用一個基礎層專案來實現所有的整合和抽象,當然也可以為不同依賴建立不同專案。
建議折中處理,為核心基礎依賴建立單獨專案,比如:Entity Framework Core;另外建立一個公共基礎專案存放其他基礎設施。
啟動模板中包含兩個專案對 Entity Framework Core 進行整合:
Bcvp.Blog.Core.EntityFrameworkCore:EF Core核心基礎依賴專案,包含:資料上下文、資料庫對映、EF Core倉儲實現等。
其他專案
還有一個專案 Bcvp.Blog.Core.DbMigrator,一個簡單的控制檯應用程式,當你執行它時,會遷移資料庫結構並初始化種子資料。這是一個有用的實用程式,可以在開發和生產環境中使用它。
專案依賴關係
Domain.Shared 其他項⽬直接或間接引⽤,項⽬中定義的型別在所有項⽬中共享。
Domain 只引⽤ Domain.Shared ,⽐如:在 Domain.Shared 中定義的 IssuType 列舉型別需要 在 Domain 項⽬中 Issue 實體中⽤到。
Application.Contracts 依賴 Domain.Shared ,這樣我們可以在 DTOs 中使⽤這些共享型別。 ⽐如: CreateIssueDto 中可以直接使⽤ IssueType 列舉。
Application 依賴 Application.Contracts ,因為 Application 實現 Application.Contracts 中定義的服務接⼝和使⽤ DTO 物件。同時,引⽤ Domain 項⽬,在應 ⽤服務中使⽤倉儲接⼝或領域物件。
EntiryFrameworkCore 依賴 Domain ,對映 Domain 物件(實體和值型別)到資料庫表 (ORM)並實現在 Domain 中定義的倉儲接⼝。
HttpApi 依賴 Application.Contract ,在控制器在內部對 應⽤服務接⼝ 進⾏依賴注⼊。
HttpApi.Client 依賴 Application.Contract 消費應⽤服務 Web 依賴 HttpApi ,釋出⾥⾯定義的 HTTP APIs 。另外,通過這種⽅式,它間接地依賴於 Application.Contracts 項⽬,可以在⻚⾯/元件中使⽤應⽤服務
DDD通用原則
在正式開始之前我們在梳理一下DDD的通用原則。
資料庫(Database Provider / ORM)獨⽴性原則
領域層和應⽤層不知道項⽬中使⽤的 ORM 和 Database Provider。只依賴於倉儲接⼝,並且倉儲接⼝ 不適合使⽤⽤任何 ORM 特殊物件
這⼀原則的主要原因是:
-
使領域層和應⽤層與基礎層獨⽴,因為基礎層將來可能更改,或者你可能需要⽀持其他型別資料庫。
-
使領域和應⽤聚焦在業務程式碼上,通過將基礎設施實現細節隱藏於倉儲之後,使您的領域和應⽤服 務專注於業務程式碼。
-
易於⾃動化測試,因為可以通過倉儲接⼝模擬倉儲資料。
關於資料庫獨⽴性原則的討論
假設你當前使⽤ Entity Framework Core 操作關係型資料庫,後期希望切換為 MongoDB,這就決定你不能使⽤ EF Core 中獨 有功能,因為在MongoDB中不被⽀持.
舉個例⼦:
不能使⽤更改跟蹤(Change Tacking),因為 MongoDB 不⽀持。所以,需要顯式更改實體。
不能在實體中使⽤導航屬性(Navigation Properties) 或集合關聯其他聚合,因為可能在⽂檔數 據庫中不⽀持。
那麼如何解決實體關聯的問題?記住規則:僅通過Id引⽤其他聚合
。
如果你認為這些功能對你很重要,⽽且你永遠不會棄⽤ EF Core,我們認為這個原則是可以有彈性的, 但是我們仍然建議使⽤倉儲模式來隱藏基礎設施的實現細節
ABP Framework 為倉儲接⼝ IRepository 提供獲取 IQueryable 物件的擴充套件⽅法 GetQueryableAsync() ,使我們在使⽤倉儲時可以直接使⽤標準LINQ擴充套件⽅法。
展示技術⽆關性原則
展示層技術(UI框架)是應⽤程式中變化最多的部分,將領域層和應⽤層設計成完全不知道展示層技術的框架⾮常重要的。
這⼀原則相對容易實現,⽽ABP的啟動模板使其更加容易實現,選擇不同UI框架⾃動⽣成對應的啟動模板項⽬。
在某些場景下,你可能需要在應⽤層和展示層使⽤相同的邏輯。舉例,你可能需要在兩個層中進⾏驗證和授權。在UI層檢測是為了提⾼⽤戶體驗,在應⽤層和領域層是出安全和資料有效性考慮。這是⾮常正常和必要的。
聚焦狀態變化,⽽不是效能優化
DDD聚焦領域物件如何變化和如何互動;如何建立實體和改變屬性,並且保持資料的完整性、有效性; 如何建立⽅法,實現業務規則。
DDD沒有考慮報表和⼤規模查詢等需要⾼效能的業務場景,如果你的應⽤程式中沒有花哨的儀表盤或報表功能,誰會去考慮呢?意思是我們需要⾃⼰考慮效能問題。
效能優化或技術選型,只要不影響到業務邏輯,可以⾃由使⽤ SQL Server 全部功能。
結語
本節知識點:
- 1.講解Abp和DDD的分層架構介紹
- 2.很重要的知識點DDD聚焦狀態變化而非效能優化
聯絡作者:加群:867095512 @MrChuJiu