產品代碼都給你看了,可別再說不會DDD(四):代碼工程結構

来源:https://www.cnblogs.com/davenkin/archive/2023/08/26/ddd-project-structure.html
-Advertisement-
Play Games

這是一個講解DDD落地的文章系列,作者是《實現領域驅動設計》的譯者滕雲。本文章系列以一個真實的並已成功上線的軟體項目——碼如雲(https://www.mryqr.com)為例,系統性地講解DDD在落地實施過程中的各種典型實踐,以及在面臨實際業務場景時的諸多取捨。 本系列包含以下文章: DDD入門 ...


這是一個講解DDD落地的文章系列,作者是《實現領域驅動設計》的譯者滕雲。本文章系列以一個真實的並已成功上線的軟體項目——碼如雲https://www.mryqr.com)為例,系統性地講解DDD在落地實施過程中的各種典型實踐,以及在面臨實際業務場景時的諸多取捨。

本系列包含以下文章:

  1. DDD入門
  2. DDD概念大白話
  3. 戰略設計
  4. 代碼工程結構(本文)
  5. 請求處理流程
  6. 聚合根與資源庫
  7. 實體與值對象
  8. 應用服務與領域服務
  9. 領域事件
  10. CQRS

案例項目介紹

既然DDD是“領域”驅動,那麼我們便不能拋開業務而只講技術,為此讓我們先從業務上瞭解一下貫穿本文章系列的案例項目 —— 碼如雲(不是馬雲,也不是碼雲)。如你已經在本系列的其他文章中瞭解過該案例,可跳過。

碼如雲是一個基於二維碼的一物一碼管理平臺,可以為每一件“物品”生成一個二維碼,並以該二維碼為入口展開對“物品”的相關操作,典型的應用場景包括固定資產管理、設備巡檢以及物品標簽等。

在使用碼如雲時,首先需要創建一個應用(App),一個應用包含了多個頁面(Page),也可稱為表單,一個頁面又可以包含多個控制項(Control),比如單選框控制項。應用創建好後,可在應用下創建多個實例(QR)用於表示被管理的對象(比如機器設備)。每個實例均對應一個二維碼,手機掃碼便可對實例進行相應操作,比如查看實例相關信息或者填寫頁面表單等,對錶單的一次填寫稱為提交(Submission);更多概念請參考碼如雲術語

在技術上,碼如雲是一個無代碼平臺,包含了表單引擎、審批流程和數據報表等多個功能模塊。碼如雲全程採用DDD完成開發,其後端技術棧主要有Java、Spring Boot和MongoDB等。

碼如雲的源代碼是開源的,可以通過以下方式訪問:

碼如雲源代碼:https://github.com/mryqr-com/mry-backend

代碼工程結構

碼如雲,我們經常會受邀去給其他公司或組織分享DDD的落地實踐經驗,分享期間聽眾一般會問很多問題,被問得最多的反倒不是限界上下文如何劃分,聚合如何設計等DDD重點議題,而是DDD工程結構該怎麼搭,包該怎麼分這些實實在在的問題。

事實上,DDD並未對工程結構做出要求,在碼如雲,我們結合行業通用實踐以及自身對DDD的認識搭建出了一套適合於自身的工程結構,我們認為對於多數項目也是適用的,在本文中,我們將對此做詳細講解。

在上一篇戰略設計中我們提到,碼如雲是一個單體項目,其通過Java分包的方式劃分出了3個限界上下文,即3個模塊。對於正在搞微服務的讀者來說,可不要被“單體”二字嚇跑了,本文所講解的絕大多數內容既適合於單體,也適合於微服務。

以上是碼如雲工程的目錄結構,在根分包src/main/java/com/mryqr下,分出了coreintegrationmanagement3個模塊包,分別對應“核心上下文”、“集成上下文”和“後臺管理上下文”,對於微服務系統來說,這3個分包則不存在,因為每個分包都有自己單獨的微服務項目,也即DDD的限界上下文和微服務存在一一對應的關係。與這3個模塊包同級的還有一個common包,該包並不是一個業務模塊,而是所有模塊所共用的一些基礎設施,比如Spring的配置、郵件發送機制等。在src目錄下,還包含testapiTestgatling三個目錄,分別對應單元測試,API測試和性能測試代碼。此外,deploy目錄用於存放與部署相關的文件,doc目錄用於存放項目文檔,gradle目錄則用於存放各種Gradle配置文件。

分包原則:先業務,後技術

在以上提及的各種模塊包中,程式員們最為關註的估計是core包之下應該如何進一步分包了,因為core是整個項目的核心業務模塊。

在做分包時,一個最常見的反模式是將技術分包作為上層分包,然後在各技術分包下再劃分業務包。DDD社區更加推崇的分包方式是“先業務,後技術”,即上層包先按照業務進行劃分,然後在各個業務包內部可以再按照技術分包。

在碼如雲的core模塊包中,首先是基於業務的分包,包含app、 assignment等幾十個包,其中的app對應於應用聚合根,而assignment對應於任務聚合根,也即每一個業務分包對應一個聚合根。在每個業務分包下再做技術分包,其中包含以下子分包:

  • command:用於存放應用服務以及命令對象等,更多相關內容請參考應用服務與領域服務
  • domain:用於存放所有領域模型,更多相關內容請參考聚合根與資源庫
  • eventhandler:用於存放領域事件處理器,更多相關內容請參考領域事件;
  • infrastructure:用於存放技術基礎設施,比如對資料庫的訪問實現等;
  • query:用於存放查詢邏輯,更多相關內容請參考CQRS

在這些分包下,可以根據實際情況進一步分包。

這種“先業務,後技術”的分包方式有以下好處:

  • 業務直觀:所有的業務模塊被放在一起,並且處於一個分包級別中,讓人一眼即可全景式地瞭解一個軟體項目中的所有業務。事實上,Robert C. Martin(Bob大叔)提出了一個概念叫尖叫架構(Screaming Architecture)講的就是這個意思。尖叫即“哇的一聲”的意思,比如當你看到一棟房子時,你會說“哇,好一棟漂亮的房子!”,也即你一眼就能識別出這是一套房子。
  • 便於導航:當你要查找一個功能時,你首先想到的一定是該功能屬於哪個業務板塊,而不是屬於哪個Controller,因此你可以先找到業務分包,然後順藤摸瓜找到相應的功能代碼。
  • 便於遷移:每一個業務包都包含了從業務到技術的所有代碼,因此在遷移時只需整體挪動業務包即可,比如,如果碼如雲以後要遷移到微服務架構,那麼只需將需要遷出的業務包整體拷貝到新的工程中即可。

在以上子分包中,domain分包應是最大的一個分包,因為其中包含了所有的領域模型以及業務邏輯。在碼如雲項目的app業務包下,各個子分包所包含的代碼量統計如下:

可以看到,domain包中所包含的代碼量遠遠超過其他所有分包的總和。當然,我們並不是說所有DDD項目都需要滿足這一點,而是強調在DDD中領域模型應該是代碼的主體。

接下來,讓我們來看看各個子分包中都包含哪些內容,首先來看domain分包:

domain分包中,最重要的當屬App聚合根了,除此之外還包含領域服務AppDomainService,工廠AppFactory和資源庫AppRepository。這裡的AppRepository是一個介面,其實現在infrastructure分包中。基於內聚原則,有些密切聯繫的類被放置在了下一級子分包中,比如attributepage分包等。值得一提的是,用於存放領域事件的event包也被放置在了domain下,因為領域事件也是領域模型的一部分,不過領域事件的處理器類則放在了與domain同級的eventhandler包中,我們將在 領域事件中對此做詳細講解。

command包用於放置應用服務以及請求數據類,這裡的“command”即CQRS中的“C”,表示外界向軟體系統所發起的一次命令。

command包中,應用服務AppCommandService用於接收外界的業務請求(命令)。AppCommandService接收的輸入參數為Command對象(以“Command”為尾碼),Command對象通過其名稱表達業務意圖,比如CopyAppCommand用於拷貝應用(這裡的“應用”表示業務上的應用聚合根),CreateAppCommand用於新建應用

eventhandler用於存放領域事件的處理器類,這些類的地位相當於應用服務,它們並不是領域模型的一部分,只是與應用服務相似起編排協調作用。

infrastructure用於存放基礎設施類,主要包含資源庫的實現類:

query用於存放與數據查詢相關的類,這裡的"query"也即CQRS中的“Q”,我們將在本系列的CQRS中對此做詳細講解。

自動化測試

自動化測試包含單元測試、API測試和性能測試。在API測試中,資料庫和消息隊列等基礎設施均通過本地Docker完成搭建,測試時先啟動整個Spring進程,然後模擬前端向各個API發送真實業務請求,最後驗證返回結果,如果遇到有需訪問第三方系統的情況,則通過Stub類進行代替。碼如雲採用的是“API測試為主,單元測試為輔”的測試策略,其API測試覆蓋率達到了90%,所有的業務用例和重要分支都有API測試覆蓋,單元測試主要用於測試領域模型,對於諸如應用服務、Controller以及事件處理器等結構性設施則不作單元測試要求,因為這些類並不包含太多邏輯,對這些類的測試可以消化在API測試中。

總結

本文主要講解了DDD代碼工程的典型目錄結構,我們推薦通過“先業務,後技術”的方式進行分包,這樣使得項目所體現的業務更加的直觀。此外,在DDD項目中,領域模型應該是整個項目的主體,所有的領域對象和業務邏輯均應該包含在domain包下。在下文請求處理流程中,我們將對DDD項目中請求處理的全流程進行詳細講解。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 上一篇介紹了`DataFrame`的顯示參數,主要是對`DataFrame`中值進行調整。 本篇介紹`DataFrame`的顯示樣式的調整,顯示樣式主要是對錶格本身的調整,比如顏色,通過顏色可以突出顯示重要的值,觀察數據時可以更加高效的獲取主要信息。 下麵介紹一些針對單個數據和批量數據的樣式調整方式 ...
  • 在Java語言中,創建線程並不像創建對象一樣簡單。雖然只需要使用new Thread()即可創建線程,但實際上創建線程比創建對象複雜得多。創建對象只需在JVM的堆中分配記憶體,而創建線程需要調用操作系統內核的API,併為線程分配一系列資源,這個成本相對較高。因此,線程被視為重量級的對象,應儘量避免頻繁... ...
  • 最近閱讀了《ASP.NET Core 技術內幕與項目實戰——基於DDD與前後端分離》(作者楊中科)的第八章,對於Core入門的我來說體會頗深,整理相關筆記。 JWT:全稱“JSON web toke”,目前流行的跨域身份驗證解決方案; 標識框架(identity):由ASP.NET Core提供的框 ...
  • 在項目中經常會遇到類似如下要求的需求,創建允許自由拖動的控制項,這樣的需求可以使用WPF的裝飾器Adorner來實現。 一、什麼是裝飾器? 裝飾器是一種特殊類型的FrameworkElement,裝飾器始終呈現在被裝飾元素的頂部,用於向用戶提供可視化提示。裝飾器可以在不改變原有控制項結構的基礎上,將功能 ...
  • ## 一:背景 ### 1. 講故事 我發現有很多的 .NET程式員 寫了很多年的代碼都沒弄清楚什麼是 `虛擬地址`,更不用談什麼是 `物理地址` 以及Windows是如何實現地址映射的了?這一篇我們就來聊一聊這兩者之間的聯繫。 ## 二:地址映射研究 ### 1. 找虛擬地址 怎麼去找 `虛擬地址 ...
  • ## 一、什麼是SignalR: **SignalR** 是用於構建需要實時用戶交互或實時數據更新的Web 應用程式的一個開放源代碼.NET 庫。不僅僅用在Web應用中,後面會講到它的應用範圍。它簡化了簡化了構建實時應用程式的過程,包括**ASP.NET Server**庫和**JavaScript ...
  • 哈嘍大家好,我是鹹魚 在《[SELinux 入門 pt.1](https://mp.weixin.qq.com/s?__biz=MzkzNzI1MzE2Mw==&mid=2247486365&idx=1&sn=4b81b3cc70b085eec6f0a595fda719fb&chksm=c2930b ...
  • 1.打開電源 (1)x86 PC開機時CPU處於實模式,實模式的定址方式是CS:IP (CS左移4位+IP) (2)開機時段寄存器CS=0xFFFF,偏移量IP=0x0000,段寄存器左移4位加上偏移量是實際地址,也就是定址地址為0xFFFF0 (ROM BIOS映射區) (3)檢查RAM,鍵盤,顯 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...