聊一聊DDD應用的代碼結構


本文想要探討的一個問題是:ddd類型的應用,代碼結構大致應該是怎么樣的 ?

Eric Evans在他的《領域驅動設計》一書中提到,領域驅動設計的一個通用的架構一般包含了 4 個概念層

  1. 用戶界面層(Interfaces):負責向用戶展現信息以及解釋用戶命令。
  2. 應用層(Application):很薄的一層,用來協調應用的活動。它不包含業務邏輯。
  3. 領域層(Domain):本層包含關於領域的信息。這是業務軟件的核心所在。
  4. 基礎設施層(Infrastructure):本層作為其他層的支撐庫存在。它提供了層間的通信,實現對業務對象的持久化,包含對用戶界面層的支持庫等作用。

我們再來看一下官方提供的一個ddd案例應用的項目結構( https://github.com/citerus/dddsample-core ) :
image.png

可以看到,這里基本上是按照Eric Evans書中的指導思想,在應用頂層包路徑(com.citerus.dddsamle)下,分了5個主要的包,對應《領域驅動設計》書中說的4層。

另外包到底應該怎么分,也存在另外一種說法,就是要按照業務功能來分(by-feature or by-business)而不是按照技術角度或者文件類型來分(by-tech or by-type),比如上面的分包就是按照技術層次在分,同理dto、service等包,就是按照文件類型在分包。而如果按照業務功能來分包,那對應的包應該就是同一塊業務歸到一個包下,例如一個博客系統,可能頂層應該是post、user、comment等包。

按照業務來分包的思路在網上占絕對優勢,大概理由有如下幾點:

  1. 要添加或者移除一塊業務時通常更加方便。比如大的應用做拆分,一般都是按照業務功能拆分的,則直接拆出某個包到新應用即可
  2. 通過應用的包結構目錄,就能大致知道這個應用在做什么。類似上例說的博客系統的 user、post、comment等包。
  3. 要改一個業務功能,可以非常快的定位到某個包下。

我們再來仔細想一想這個問題,分包只是將一些文件歸類到一處,方便查找,同時也提供了類似目錄的功能,讓人看到包列表就知道系統大概做什么。其實就是一個分類/歸類的思想。

再來考慮下java應用中可以用於分類的技術手段:

  1. java應用。一個java應用自身就是一塊業務。
  2. maven-module(jar包)。maven-module是應用內部頂級的分類,是相對較大粒度的分類,同時maven-module有個好處:可以通過依賴聲明,嚴格控制模塊之間的依賴關系。
  3. java-package(代碼包)。java-package是相對較小粒度的分類,只是將一類class或packge歸類到一起,有點類似文件夾的功能,同時也用於防止類名包名沖突。

最大粒度的分類就是java應用,一個應用一般對應一塊業務; 其次的粒度是maven模塊,maven模塊下還可以繼續套maven模塊(一些非常大的應用可能就是這樣套的);最后是java包,java包是可以套java包的,越上層的java包,分類的粒度越大。

基於上述這些知識和分析,這里總結一下自己覺得比較好的一種代碼組織的方式

  1. 如果應用定位是個大應用,有多個Bounded-Context,那應用可以用maven划分成不同的業務模塊;每個業務模塊對應一個BC,BC內部再用maven划分成不同的ddd的層次。first module-by-biz,then module-by-tech

  2. 如果應用已經做了微服務拆分,整個應用就是一個Bounded-Context,則在應用內頂層按照ddd的層次,划分成interfaces、applicaiton、domain、infrastructure層。之所以用maven-module而不是java-package來划分,是因為maven-module可以控制依賴關系,這樣你在domain層中寫代碼時,就不可能引用到其他層的類或接口。

  3. 在interfaces、app、domain、infra層內部,分包視情況而定,by-biz或者by-tech都是可行的,也可以先by-biz再by-tech 或先by-tech再by-biz。比如官方ddd應用中,在interfaces層就是先by-biz分包,再by-tech分包。 另外這里個人的建議是:對於domian.model層,因為這層本身就是強調要展現你的業務,一一對應地描述你的業務,所以最好是by-biz來分包,並且最好每個包就是ddd中的一個聚合。 對於infra包,因為這層大部分是技術相關的,by-tech分比較合適。

image.png

最后,再說一下按照ddd書中的建議分包之后,每個包下面大概放哪些內容。

    1. interfaces:負責對外交互,包括WEB服務、遠程接口(HSF接口實現)、Web應用(MVC中的Controller)、批處理前台。處理輸入解析、驗證、轉換。也處理輸出的序列化(包括通過WEB請求的輸出HTML、JSON,遠程Facade的DTO)
      不是MVC中的View。
    2. application:驅動程序流程,處理具體user case。這里的操作與interface不關聯(即同樣的一個Service應可被不同的interface(Web、遠程調用、異步消息)復用)。這層也適合處理事務、高層次日志(oplog)、安全(權限)。注意:這層不應該有領域邏輯,它只是協調domain層的對象完成真正的工作。
      Domain event的監聽注冊在這層。
    3. domain:程序的核心。通常每一個聚合(aggregate)一個package。聚合包含實體(entity),值對象(value object),領域事件(domain event),資源庫(repository,僅接口)接口和一些工廠(Factory)。
      Anti-corruption layer的interface應在這
      Domain event的觸發在這。
    4. Infrastructure:通過不同方式輔助上面3層。如repository的implementation(ibatis,hibernate, nosql),anti-corruption layer的implementation,消息隊列(Metaq, Notify)的后端。


免責聲明!

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



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