領域驅動設計(DDD):項目目錄(包、模塊)結構


項目目錄(包、模塊)結構

在項目的開發階段,目錄結構的划分往往被看做是邁向成功的第一步。這一步的邁出往往伴隨着很多方面的權衡(考量),總的來說是兩個方面的考量:業務方面和技術方面。

  • 業務方面的考量包括:限界上下文、子域、業務模塊。
  • 技術方面的考量包括:軟件架構(分層架構、六邊形架構)、構造型分類。

目錄結構構成

常見的項目的目錄結構基本上由:領域名(domain)、層名(layer)、構造型名(stereotype)、業務模塊名(module)這四個部分組成。

領域(業務域、子域)名稱

在《領域驅動設計》中的領域通常是指一個業務域,是一個特定的業務范圍。同類項目中的業務可能雷同,但對於大多數的項目要解決的業務(問題)來說不會超出所在業務域的范圍,因此在項目的目錄(包、模塊)結構中包含業務域的名稱能起到限界作用。比如:產品目錄子域(Catalog)、訂單子域(Order)、物流子域(Shipping)、發票子域(Invoice)等等。

分層架構中層次名稱

在項目的目錄結構中顯式的引入層名是一種技術考量,更具體一些是編碼的考量。分層架構是一種從混亂到有序的解決方案(架構模式)。它的做法是將一個應用程序(流程)划分為多組子任務,其中每組子任務都位於特定抽象層中。例如:分層架構在應用系統的后端開發中,常將一個應用系統划分為三層架構或者四層架構。

三層架構:

  • 表現層(Presentation)
  • 業務邏輯層(Business)
  • 持久層(Persistence)

四層架構:

  • 表現層(Presentation)
  • 應用(邏輯)層(Application)
  • 領域層(Domain)
  • 基礎設施層(Infrastructure)

在四層架構中的基礎設施層要比三層架構中的持久層的功能多一些。

在應用系統開發中的分層架構並不是嚴格意義上的分層架構。真正的分層表示為上層只能依賴下層,是單向依賴,不能存在雙向依賴。具體來說有以下特點:

  • J 層依賴 J - 1 層,J + 1 層依賴 J 層。
  • J - 1 層不會依賴 J 層,J 層也不會依賴 J + 1 層。
  • J + 1 層也不會依賴 J - 1 層。

層與層之間通過數據的封裝、轉換或者直接使用來做到隔離。

在追求性能和靈活性方面,出現了兩種分層變種:寬松的分層系統(Relaxed Layered System)和通過繼承進行分層(Layering Through Inheritance)。我們將簡要討論 寬松的分層系統,因為普遍地應用系統采用的就是寬松的分層系統。

寬松的分層系統表示:每層都可以使用它的下層服務,而不僅僅是下一層的服務。每層都可能是半透明的,這意味着有些服務只對上一層可見,而有些服務對上面的所有層都可見。

 

 

就算是嚴格地分層架構或者寬松的分層架構,都表明是上層依賴下層。但在實際地應用系統的架構中並沒有完全遵循這種依賴關系,因為架構人員需要在整體架構與分層架構之間進行搖擺球式思考。比如:在領域(Domain)層中的 Repository 接口的實現類往往會放在基礎設施(Infrastructure)層中,這顯然違反了分層架構中的單向依賴關系。這樣的問題有兩種解決方案:一是誠然接受。二是將領域層中的 Repository 接口的實現類放置在領域層內。

構造型名稱(stereotype)

構造型使用書名號(<<>>)來表示,用於區分不同地建模元素。

 

 

如:實體(entity)、枚舉(enumeration)、異常(exception)、查詢(query)、事件(event)、資源庫(repository)、服務(service)、控制器(controller)等等都是常見的構造型。

補充:在 UML1.4 及以后版本允許一個建模元素可以附加多個構造型。

有些項目在划分項目的目錄結構時,會將構造型顯式的引入到目錄(包)結構中,這是一種歸類的組織方式。

業務模塊名稱(module)

在分析一個業務(問題)域時,會將一個業務域划分為多個業務模塊。比如在商店(Store)子域中會被划分為:商店員工(Staff)、商店會員(Member)、商店角色(Role)等等。

目錄結構分類

在分別對目錄(包)結構的構成元素做了簡單介紹后,下面要開始具體探討由這些元素組合而成的目錄結構了。

  • 業務域名.層名.*
  • 業務域名.層名.業務模塊名.*
  • 業務域名.構造型名.*
  • 業務域名.構造型名.業務模塊名.*
  • 業務域名.(層名 & 構造型名).*
  • 業務域名.(層名 & 構造型名).業務模塊名.*
  • 業務域名.業務模塊名.*
  • 業務域名.業務模塊名.層名.*
  • 業務域名.業務模塊名.構造型名.*
  • 業務域名.業務模塊名.(層名 & 構造型名).*
  • 業務域名.(業務模塊名 & 層名 & 構造型名).*
  • (構造型名 || 層名).業務域名.業務模塊名.*

補充說明:

  • 省略包(package)的反向域名(org.mallfoundry.*)前綴的部分。
  • 領域看作是業務域,其中業務域名是業務域 名,而不是業務 域名。
  • (層名 & 構造型名)是一種混合,表示在同一級別的目錄(包)結構上同時存在按層和按構造型划分的兩種方式。

目錄結構:業務域名.層名.*

├─catalog            // 商品目錄子域
│  ├─application
│  ├─domain
│  ├─infrastructure
│  └─presentation
├─order              // 訂單子域
│  ├─application
│  ├─domain
│  ├─infrastructure
│  └─presentation
└─store              // 商家子域
    ├─application
    ├─domain
    ├─infrastructure
    └─presentation

業務域名.層名.業務模塊名.*

├─catalog            // 商品目錄子域
│  ├─application        // 應用層
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─domain             // 領域層
│  │  ├─brand               // 商品品牌模塊
│  │  ├─category            // 商品類目模塊
│  │  ├─collection          // 商品集合模塊
│  │  └─product             // 商品模塊
│  ├─infrastructure     // 基礎設施層
│  │  └─persistent          // 持久化
│  │      ├─jpa
│  │      ├─mybatis
│  │      └─redis
│  └─presentation       // 表現層
│      ├─graphql
│      ├─grpc
│      ├─rest
│      ├─view
│      └─websocket
└─order
    ├─application
    │  ├─dispute
    │  ├─review
    │  ├─shipping
    │  └─source
    ├─domain
    │  ├─dispute
    │  ├─review
    │  ├─shipping
    │  └─source
    ├─infrastructure
    └─presentation

業務域名.構造型名.*

├─catalog
│  ├─controller
│  ├─exception
│  ├─model
│  ├─query
│  ├─repository
│  └─service
└─order
    ├─controller
    ├─exception
    ├─model
    ├─query
    ├─repository
    └─service

備注:

  • Model 包中中包含:實體、值對象、枚舉等領域模型。在有些項目中會將 Model 包命名為:Pojo、Bean、Entity 等。
  • 在按構造型划分目錄時還會存在:DTO、VO 等包結構。

業務域名.構造型名.業務模塊名.*

├─catalog
│  ├─controllers
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─exceptions
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─models
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─queries
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─repositories
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  └─services
│      ├─brand
│      ├─category
│      ├─collection
│      └─product
└─order
    ├─controllers
    ├─exceptions
    ├─models
    ├─queries
    ├─repositories
    └─services

備注:在采用這種目錄結構時,構造型名稱常采用復數形式命名。

業務域名.(層名 & 構造型名).*

├─catalog
│  ├─controller
│  ├─dao
│  ├─exception
│  ├─model
│  ├─query
│  └─service
└─order
    ├─controller
    ├─dao
    ├─exception
    ├─model
    ├─query
    └─service

備注:在目錄(包)結構中采用(層名 & 構造型名)混合式的方式,常常出現在將分層架構與構造型混淆在一起的項目中。

  • Controller 代表表現層。
  • Service 代表業務邏輯層。
  • Dao 或者 Repository 代表數據訪問層。
  • Model 代表領域模型。

業務域名.(層名 & 構造型名).業務模塊名.*

├─catalog
│  ├─controller
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─dao
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─exception
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─model
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  ├─query
│  │  ├─brand
│  │  ├─category
│  │  ├─collection
│  │  └─product
│  └─service
│      ├─brand
│      ├─category
│      ├─collection
│      └─product
└─order
    ├─controller
    ├─dao
    ├─exception
    ├─model
    ├─query
    └─service

業務域名.業務模塊名.*

├─catalog
│  ├─brand
│  ├─category
│  ├─collection
│  └─product
└─order
    ├─dispute
    ├─review
    ├─shipping
    └─source

業務域名.業務模塊名.層名.*

├─catalog
│  ├─brand
│  │  ├─application
│  │  ├─domain
│  │  ├─infrastructure
│  │  └─presentation
│  ├─category
│  │  ├─application
│  │  ├─domain
│  │  ├─infrastructure
│  │  └─presentation
│  ├─collection
│  │  ├─application
│  │  ├─domain
│  │  ├─infrastructure
│  │  └─presentation
│  └─product
│      ├─application
│      ├─domain
│      ├─infrastructure
│      └─presentation
└─order
    ├─dispute
    ├─review
    ├─shipping
    └─source

備注:分層會在模塊內部。

業務域名.業務模塊名.構造型名.*

├─catalog
│  ├─brand
│  │  ├─controller
│  │  ├─exception
│  │  ├─model
│  │  ├─query
│  │  ├─repository
│  │  └─service
│  ├─category
│  │  ├─controller
│  │  ├─exception
│  │  ├─model
│  │  ├─query
│  │  ├─repository
│  │  └─service
│  ├─collection
│  │  ├─controller
│  │  ├─exception
│  │  ├─model
│  │  ├─query
│  │  ├─repository
│  │  └─service
│  └─product
│      ├─controller
│      ├─exception
│      ├─model
│      ├─query
│      ├─repository
│      └─service
└─order
    ├─dispute
    ├─review
    ├─shipping
    └─source

備注:構造型會在模塊內部。

業務域名.業務模塊名.(層名 & 構造型名).*

├─catalog
│  ├─brand
│  │  ├─controller
│  │  ├─dao
│  │  ├─exception
│  │  ├─model
│  │  ├─query
│  │  └─service
│  ├─category
│  │  ├─controller
│  │  ├─dao
│  │  ├─exception
│  │  ├─model
│  │  ├─query
│  │  └─service
│  ├─collection
│  │  ├─controller
│  │  ├─dao
│  │  ├─exception
│  │  ├─model
│  │  ├─query
│  │  └─service
│  └─product
│      ├─controller
│      ├─dao
│      ├─exception
│      ├─model
│      ├─query
│      └─service
└─order
    ├─dispute
    ├─review
    ├─shipping
    └─source

業務域名.(業務模塊名 & 層名 & 構造型名).*

├─catalog
│  ├─brand
│  ├─category
│  ├─collection
│  └─product            // Product 模塊
│      ├─controller
│      ├─dao
│      ├─exception
│      ├─model
│      ├─query
│      ├─review             // Product 模塊內的 Review 模塊
│      │  ├─controller
│      │  ├─dao
│      │  ├─exception
│      │  ├─model
│      │  ├─query
│      │  └─service
│      └─service
└─order
    ├─dispute
    ├─review
    ├─shipping
    └─source

備注:(業務模塊名 & 層名 & 構造型名)三者混合是一種項目目錄(包)划分的方式。這種結構往往看上去會有些混亂。

(構造型名 || 層名).業務域名.業務模塊名.*

├─catalog
│  ├─brand
│  ├─category
│  ├─collection
│  └─product
├─order
│  ├─dispute
│  ├─review
│  ├─shipping
│  └─source
└─rest                  // 發布 RESTful 接口。
    ├─catalog
    │  ├─brand
    │  ├─category
    │  ├─collection
    │  └─product
    └─order
        ├─dispute
        ├─review
        ├─shipping
        └─source

使用 Module 橫向分割

在 Java 中會有 jar 的形式來組織模塊(Module),我們可以使用 Module 先橫向分割,然后在模塊內部再划分目錄結構。

先將產品目錄(Catalog)子域和訂單(Order)子域橫向分割成四個模塊:

  • catalog
  • catalog-rest
  • order
  • order-rest

Module:catalog

└─org.mallfoundry.catalog
    ├─brand
    ├─category
    ├─collection
    └─product

Module:catalog-rest

└─org.mallfoundry.rest.catalog
    ├─brand
    ├─category
    ├─collection
    └─product

Module:order

└─org.mallfoundry.order
    ├─dispute
    ├─review
    ├─shipping
    └─source

Module:order-rest

└─org.mallfoundry.rest.order
    ├─dispute
    ├─review
    ├─shipping
    └─source

重名混淆(業務模塊名 & 層名 & 構造型名)

在初次瀏覽一個不熟悉的項目時,可能會對(業務模塊名 & 層名 & 構造型名)這三種結構發生重名混淆。

在一個以業務模塊名為主的目錄(包)結構中出現像 .repository. 、 .dao. 這樣的目錄結構時,你可能瞬間想到是構造型或者分層。但是 .repository. 、 .dao. 最大可能只是在表示一個 repository 或者 dao 的業務模塊。

開源電商

Mallfoundry 是一個完全開源的使用 Spring Boot 開發的多商戶電商平台。它可以嵌入到已有的 Java 程序中,或者作為服務器、集群、雲中的服務運行。

  • 領域模型采用領域驅動設計(DDD)、接口化以及面向對象設計。

項目地址:

總結

由:領域名(domain)、層名(layer)、構造型名(stereotype)、業務模塊名(module)這四個部分組成的目錄(包)結構是項目中常采用的。同時在划分目錄結構時也可以使用 Module 先進行橫向切割的方式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM