DDD領域驅動與傳統CRUD

DDD 是一套 應對復雜業務系統 的設計方法論,核心是 讓代碼直接映射業務邏輯,避免技術實現與業務需求脫節。
關鍵區別

  • 傳統開發:根據數據庫表寫 CRUD(技術驅動)。
  • DDD:根據業務行為建模(業務驅動)。

舉例

  • 傳統方式:設計?orders?表 → 寫?OrderDAO?→ 實現增刪改查。
  • DDD 方式:先分析業務如何描述“訂單”(如“下單”、“取消”、“支付超時”)→ 再建模為?Order?對象的行為。

貧血模型與充血模型

本質特征

特征貧血模型充血模型
業務邏輯存放位置Service層領域對象(Entity/Value Object)內部
領域對象職責僅作為數據載體(DTO)封裝數據和行為
代碼表現大量Getter/Setter,無業務方法包含業務方法
適用場景簡單CRUD系統復雜業務系統(DDD推薦)

代碼實現對比

貧血模型實現

  • Order?只是數據的容器,業務邏輯散落在?Service?中。
  • 當業務復雜時,Service?會變成“上帝類”(God Class)。
// 1. 貧血的Order類(僅有數據)
public class Order {private String orderId;private String productId;private int quantity;private String status;// 只有Getter/Setter,無業務邏輯public String getOrderId() { return orderId; }public void setOrderId(String orderId) { this.orderId = orderId; }// 其他Getter/Setter省略...
}// 2. 業務邏輯全在Service中
@Service
public class OrderService {public void createOrder(String productId, int quantity) {Order order = new Order();order.setOrderId(UUID.randomUUID().toString());order.setProductId(productId);order.setQuantity(quantity);order.setStatus("CREATED");// 校驗邏輯也在Service中if (quantity <= 0) {throw new IllegalArgumentException("Quantity must be positive");}orderRepository.save(order);}
}

充血模型實現

  • Order?自己負責業務規則(如創建校驗、狀態轉換)。
  • 業務邏輯高內聚,Service層變薄。
// 1. 充血的Order類(封裝數據和行為)
public class Order {private String orderId;private String productId;private int quantity;private OrderStatus status; // 枚舉// 私有構造方法,強制通過工廠方法創建private Order(String productId, int quantity) {this.orderId = UUID.randomUUID().toString();this.productId = productId;this.quantity = quantity;this.status = OrderStatus.CREATED;}// 靜態工廠方法(封裝創建邏輯)public static Order create(String productId, int quantity) {if (quantity <= 0) {throw new IllegalArgumentException("Quantity must be positive");}return new Order(productId, quantity);}// 領域行為public void cancel() {if (this.status == OrderStatus.PAID) {throw new IllegalStateException("Paid order cannot be cancelled");}this.status = OrderStatus.CANCELLED;}
}// 2. 應用層Service(僅協調流程)
@Service
public class OrderAppService {public void createOrder(String productId, int quantity) {Order order = Order.create(productId, quantity); // 調用領域對象orderRepository.save(order);}
}

DDD實戰

架構圖

架構設計

src/
├── presentation/    # 用戶接口層
├── application/     # 應用層
├── domain/          # 領域層
│   ├── model/       # 領域模型(實體、值對象等)
│   └── repository/  # 倉儲接口
└── infrastructure/  # 基礎設施層(簡單實現)

代碼示例

(1) 用戶接口層(Presentation Layer)

處理HTTP請求,接收用戶輸入。

// presentation/OrderController.java
@RestController
@RequestMapping("/orders")
public class OrderController {private final OrderAppService orderAppService; // 依賴應用層@PostMappingpublic ResponseEntity<String> createOrder(@RequestBody CreateOrderRequest request) {orderAppService.createOrder(request.toCommand()); // 轉換為命令對象return ResponseEntity.ok("Order created");}
}// DTO:用戶請求參數
public class CreateOrderRequest {private String productId;private int quantity;// 轉換為應用層命令對象public CreateOrderCommand toCommand() {return new CreateOrderCommand(productId, quantity);}
}

(2) 應用層(Application Layer)

協調領域對象完成用例,不包含業務邏輯

// application/OrderAppService.java
@Service
public class OrderAppService {private final OrderRepository orderRepository; // 依賴領域層倉儲public void createOrder(CreateOrderCommand command) {// 1. 調用領域層創建訂單Order order = Order.create(command.getProductId(), command.getQuantity());// 2. 通過倉儲保存聚合根orderRepository.save(order);// 3. 可發布領域事件(此處省略)}
}// 應用層命令對象
public class CreateOrderCommand {private String productId;private int quantity;// 構造方法、getter省略...
}

(3) 領域層(Domain Layer)

核心業務邏輯,包含實體、值對象、倉儲接口等。

3.1 實體(Entity)與聚合根
// domain/model/Order.java
public class Order {private String orderId;      // 唯一標識private String productId;private int quantity;private OrderStatus status;  // 枚舉:CREATED, PAID, CANCELLED等// 私有構造方法,強制通過工廠方法創建private Order(String productId, int quantity) {this.orderId = UUID.randomUUID().toString();this.productId = productId;this.quantity = quantity;this.status = OrderStatus.CREATED;}// 工廠方法(封裝創建邏輯)public static Order create(String productId, int quantity) {if (quantity <= 0) {throw new IllegalArgumentException("Quantity must be positive");}return new Order(productId, quantity);}// 領域行為public void cancel() {this.status = OrderStatus.CANCELLED;}
}
3.2 倉儲接口(Repository)
// domain/repository/OrderRepository.java
public interface OrderRepository {void save(Order order);// 其他查詢方法省略...
}

(4) 基礎設施層(Infrastructure Layer)

實現領域層定義的倉儲接口(如數據庫操作)。

// infrastructure/persistence/OrderRepositoryImpl.java
@Repository
public class OrderRepositoryImpl implements OrderRepository {// 模擬數據庫(實際可用JPA/MyBatis等)private Map<String, Order> orders = new HashMap<>();@Overridepublic void save(Order order) {orders.put(order.getOrderId(), order);System.out.println("Order saved: " + order.getOrderId());}
}

調用流程圖

對比傳統CRUD

DDD傳統CRUD
先設計Order的行為方法直接操作orders
業務邏輯在領域層業務邏輯散落在Service中
通過聚合根保證一致性需手動校驗外鍵約束

充血模型特點

  1. 禁止Setter:通過構造方法或工廠方法創建對象。
  2. 領域方法命名:使用業務語言(如?cancel()?而非?setStatus("CANCELLED"))。
  3. 避免“貧血”的充血模型
    • 錯誤示例:在?Order?中加了方法,但核心邏輯仍在Service中。

常見誤區

  • ??認為充血模型等于“把所有代碼塞進Entity”
    • 跨聚合邏輯應放在領域服務(Domain Service)中。
  • ??忽視聚合根的一致性邊界
    • 例如訂單和庫存屬于不同聚合,不能直接在?Order?中修改庫存,應通過領域事件解耦。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/902645.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902645.shtml
英文地址,請注明出處:http://en.pswp.cn/news/902645.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

20. git diff

基本概述 git diff的作用是&#xff1a;比較代碼差異 基本用法 1.工作區 VS 暫存區 git diff [file]2.暫存區 VS 最新提交 git diff --staged [file] # 或 git diff --cached [file]3.工作區 VS 最新提交 git diff HEAD [file]高級用法 1.比較兩個提交間的差異 git dif…

大模型面經 | 春招、秋招算法面試常考八股文附答案(五)

大家好,我是皮先生!! 今天給大家分享一些關于大模型面試常見的面試題,希望對大家的面試有所幫助。 往期回顧: 大模型面經 | 春招、秋招算法面試常考八股文附答案(RAG專題一) 大模型面經 | 春招、秋招算法面試常考八股文附答案(RAG專題二) 大模型面經 | 春招、秋招算法…

Sql刷題日志(day5)

面試&#xff1a; 1、從數據分析角度&#xff0c;推薦模塊怎么用指標衡量&#xff1f; 推薦模塊主要目的是將用戶進行轉化&#xff0c;所以其主指標是推薦的轉化率推薦模塊的指標一般都通過埋點去收集用戶的行為并完成相應的計算而形成相應的指標數據&#xff0c;而這里的驅動…

封裝 element-ui 二次彈框

author 封裝 element-ui 彈框 param text 文本內容 &#xff08;不傳默認顯示 確定執行此操作嗎&#xff1f; &#xff09; param type 彈框類型&#xff08;不傳默認warning類型&#xff09; param title 彈框標題&#xff08;不傳默認顯示 提示 &#xff09; export fun…

【Rust 精進之路之第12篇-生命周期·入門】為何需要與顯式標注 (`‘a`):讓編譯器讀懂引用的“有效期”

系列: Rust 精進之路:構建可靠、高效軟件的底層邏輯 作者: 碼覺客 發布日期: 2025-04-20 引言:懸垂引用的“幽靈”與編譯器的“偵探” 在前面的章節中,我們深入學習了 Rust 的所有權系統,以及如何通過引用 (& 和 &mut) 進行借用,從而在不轉移所有權的情況下安…

[密碼學實戰]CTF競賽高頻加密與解密技術詳解

CTF競賽高頻加密與解密技術詳解 一、CTF加密體系全景圖 在CTF密碼學挑戰中&#xff0c;加解密技術主要分為四大戰域&#xff1a; #mermaid-svg-lmm07BXqYAGYjymI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lm…

docker.desktop下安裝普羅米修斯prometheus、grafana并看服務器信息

目標 在docker.desktop下先安裝這三種組件,然后顯示當前服務的CPU等指標。各種坑已踩,用的是當前時間最新的鏡像 核心關系概述 組件角色依賴關系Prometheus開源監控系統,負責 數據采集、存儲、查詢及告警。依賴 Node-Exporter 提供的指標數據。Node-Exporter專用的 數據采集…

《MySQL:MySQL表的內外連接》

表的連接分為內連接和外連接。 內連接 內連接實際上就是利用where子句對兩種表形成的笛卡爾積進行篩選&#xff0c;之前的文章中所用的查詢都是內連接&#xff0c;也是開發中使用的最多的連接查詢。 select 字段 from 表1 inner join 表2 on 連接條件 and 其他條件&#xff1…

實現SpringBoot底層機制【Tomcat啟動分析+Spring容器初始化+Tomcat 如何關聯 Spring容器】

下載地址&#xff1a; https://download.csdn.net/download/2401_83418369/90675207 一、搭建環境 創建新項目 在pom.xml文件中導入依賴 &#xff08;一定要刷新Maven&#xff09;排除內嵌的Tomcat&#xff0c;引入自己指定的Tomcat <?xml version"1.0" enco…

從零開始構建微博爬蟲:實現自動獲取并保存微博內容

從零開始構建微博爬蟲&#xff1a;實現自動獲取并保存微博內容 前言 在信息爆炸的時代&#xff0c;社交媒體平臺已經成為信息傳播的重要渠道&#xff0c;其中微博作為中國最大的社交媒體平臺之一&#xff0c;包含了大量有價值的信息和數據。對于研究人員、數據分析師或者只是…

Uniapp微信小程序:輕松獲取用戶頭像和昵稱

參考文獻&#xff1a;Uniapp微信小程序&#xff1a;輕松獲取用戶頭像和昵稱-百度開發者中心 (baidu.com) uni.login({ provider: weixin, success: function (loginRes) { console.log(loginRes.authResult); // 打印登錄憑證 // 使用登錄憑證獲取用戶信息 uni.getUserInfo({ …

【自然語言處理與大模型】大模型(LLM)基礎知識③

&#xff08;1&#xff09;大模型的“7B”是什么意思&#xff1f; "B"通常代表“Billion”&#xff0c;即十億。因此&#xff0c;當提到7B時&#xff0c;指的是該模型擁有7 billion&#xff08;70億&#xff09;個參數。 &#xff08;2&#xff09;模型后面標的“ins…

聊聊自動化用例的維護

自動化測試中的農藥悖論&#xff1a;為何長期維護至關重要 自動化測試常被視為"一次編寫&#xff0c;永久有效"的解決方案&#xff0c;但隨著時間的推移&#xff0c;即使設計最精良的測試套件也會逐漸失效。這種現象被稱為農藥悖論&#xff08;Pesticide Paradox&am…

微幀Visionular斬獲NAB Show 2025年度產品獎

在本月剛結束的NAB Show 2025展會上&#xff0c;全球領先的視頻編碼與AI超高清服務提供商微幀Visionular大放異彩&#xff0c;其核心產品AI-driven Video Compression&#xff08;AI視頻智能編碼引擎&#xff09;不僅在展會中吸引了眾多行業目光&#xff0c;更憑借其卓越的編碼…

idea中運行groovy程序報錯

我的項目是使用的 gradle 構建的。 在 idea 中運行Groovy的面向對象程序報錯如下&#xff1a; Execution failed for task :Person.main(). > Process command G:/Program Files/jdk-17/jdk-17.0.12/bin/java.exe finished with non-zero exit value 1* Try: Run with --s…

【自然語言處理與大模型】個人使用LLaMA Factory微調的記錄

一、魔塔社區免費服務器如何使用webui微調&#xff1f; 一上來我就得先記錄一下&#xff0c;使用魔塔社區的免費服務器的時候&#xff0c;因為沒有提供ssh而導致無法看到webui的遺憾如何解決的問題&#xff1f; 執行命令 如果點這個鏈接無法彈出微調的webui&#xff0c;則可以在…

【官方正版,永久免費】Adobe Camera Raw 17.2 win/Mac版本 配合Adobe22-25系列軟

Adobe Camera Raw 2025 年 2 月版&#xff08;版本 17.2&#xff09;。目前為止最新版新版已經更新2個月了&#xff0c;我看論壇之前分享的還是2024版&#xff0c;遂將新版分享給各位。 Adobe Camera Raw&#xff0c;支持Photoshop&#xff0c;lightroom等Adobe系列軟件&#…

leetcode:1295. 統計位數為偶數的數字(python3解法)

難度&#xff1a;簡單 給你一個整數數組 nums&#xff0c;請你返回其中位數為 偶數 的數字的個數。 示例 1&#xff1a; 輸入&#xff1a;nums [12,345,2,6,7896] 輸出&#xff1a;2 解釋&#xff1a; 12 是 2 位數字&#xff08;位數為偶數&#xff09; 345 是 3 位數字&…

使用Handsontable實現動態表格和下載表格

1.效果 2.實現代碼 首先要加載Handsontable&#xff0c;在示例中我是cdn的方式引入的&#xff0c;vue的話需要下載插件 let hot null;var exportPlugin null;function showHandsontable(param) {const container document.getElementById("hot-container");// 如果…

2.1 基于委托的異步編程方法

基于委托的異步編程模型是 .NET 早期版本中實現異步操作的一種方式&#xff0c;主要通過 BeginInvoke 和 EndInvoke 方法來實現。這種基于委托的異步模式已被 Task 和 async/await 模式取代&#xff0c;但在維護舊代碼時仍可能遇到這種模式。 委托的方法中&#xff1a;Invoke用…