1. 程式人生 > 其它 >大話領域驅動設計——分層架構

大話領域驅動設計——分層架構

這一篇,我們首先要分析領域驅動設計的分層架構,在實際編碼時,戰術模式下的各種概念,需要最終落地到分層架構的各個層中。

如果大家對於DDD有所瞭解,一定看到過下面這個經典的DDD四層架構圖。我們也是以經典的DDD四層架構為基礎做詳細的講解:

領域層是我們領域模型具體程式碼實現的位置,通常包含實體、聚合根、值物件、領域服務、領域事件等的具體實現,也包含倉儲的介面宣告。領域是整個應用的核心。

應用層可以理解為對於領域層的封裝及聚合,是比較輕量的一層,主要將領域層的業務物件及功能封裝成可供表示層使用的服務。應用層通常包含應用服務和資料傳輸物件。

使用者介面層也稱為表示層,為使用者提供視覺化的互動介面,是系統和使用者的介面。

基礎設施層整個系統的基礎功能支撐,包含通用的模組、方法也包含倉儲的實現。

和傳統以三層為基礎的分層架構不同,在DDD標準的四層架構裡表示層也可以直接引用領域層,但是在實際的開發裡面,我們一般不會這麼做,而是領域層的所有功能都經過應用層的物件封裝。而UI層只和應用層互動。

倉儲是對資料操作的封裝,和傳統三層架構中資料訪問作為三層架構中重要的一層(資料訪問層)不同,在DDD四層架構中,倉儲介面的宣告是在領域層,但實際的倉儲實現是在基礎設施層。之所以出現這種分層,是因為在DDD的思想中,當我們設計時不應該去關注如何對資料持久化,只需要知道有哪些資料在何時需要持久化即可。而且隨著EF等ORM框架的出現,資料持久化的程式碼也變得越來越簡單。甚至在ABP vNext框架中,我們大多數時候可以直接使用預設的倉儲介面和實現。

領域層的程式碼設計和模組劃分是按照領域模型來完成的,也就是說領域層的所有程式碼都是依據業務本身來設計。而應用層設計的時我們更多的會考慮表現層需要哪些介面或哪些資料。一個應用層的模組,可以對應一個或多個領域,一個介面也可以跨多個領域的資料和功能的整合。

但是在實際設計過程中,應用層的設計除了考慮表現層的模組劃分以外,更多的會考慮領域層的劃分。這主要考慮到我們如果要做微服務下服務的拆分時主要是依據領域劃分來拆分微服務。通常會將一個領域或者幾個關聯度較高的領域劃分成一個微服務。如果某一應用層模組同時引用A和B領域而A和B領域分別劃到了不同的服務中,這個應用模組就會無法分配。所以我們會看到很多時候領域層和服務層的模組劃分基本一致。同時為了保證領域層物件和應用層物件對映時方便,通常我們也要保證領域實體和應用層資料傳輸物件中相同意義的欄位名稱保持一致。

在ABP vNext框架中,領域層包含Domain和Domain.Share兩個專案,Domain為領域層的核心專案,以資料夾劃分領域。包含Entity(實體)、AggregateRoot(聚合根)、ValueObject(值物件)、IDomainService(領域服務介面)、DomainService(領域服務實現)、IRepository(倉儲介面)及解決方案的其他領域物件,Domain.Share專案包含常量,列舉和其他物件,這些物件實際上是領域層的一部分,但是解決方案中所有的專案中都會使用到。

應用層包含Application和Application.Contracts兩個專案。Application.Contracts專案包含DTO(資料傳輸物件)和IApplicationService(應用服務介面),Application專案包含ApplicationService(應用服務實現)。

ABP vNext在基礎設施層提供的專案為EntityFrameworkCore和MongoDB,分別對應倉儲在EF和MongoDB下的實現。

ABP vNext為表現層提供的專案為Web,是MVC下表現層的實現。對於前後端分離專案,後端不包含表現層,而使用HttpApi.Host作為啟動項,該專案是作為服務宿主存在,主要提供WebApi所需要的服務監聽和管道模型。

實際在專案引用關係中,EntityFrameworkCore專案引用的是DomainService專案,這個和開頭部分那張圖是相反的。原因是倉儲介面定義在Domain專案中而實現定義在EntityFrameworkCore專案中,通過依賴注入的方式注入到上層需要呼叫的模組中。