領域驅動設計(DDD)【26】之CQRS模式初探

文章目錄

  • 一 CQRS初探:理解基本概念
    • 1.1 什么是CQRS?
    • 1.2 CQRS與CRUD的對比
    • 1.3 為什么需要CQRS?
  • 二 CQRS深入:架構細節
    • 2.1 基本架構組成
    • 2.2 數據流示意圖
  • 三 CQRS實戰:電商訂單案例
    • 3.1 傳統CRUD方式的訂單處理
    • 3.2 CQRS方式的訂單系統
      • 3.2.1 命令端實現
      • 3.2.2 查詢端實現
      • 3.2.3 讀模型DTO
    • 3.3 數據同步實現
  • 四 CQRS進階:高級話題
    • 4.1 最終一致性處理
    • 4.2 CQRS的適用場景
  • 五 CQRS實踐建議
    • 5.1 實施步驟
    • 5.2 常見陷阱與解決方案
    • 5.3 性能考量
  • 六 總結

一 CQRS初探:理解基本概念

1.1 什么是CQRS?

  • Greg Young把增、刪、改功能稱為 Command(命令),把查詢稱為 Query,這兩種功能的職責不同,應該采用不同的方式來處理,因此叫做“命令查詢職責分離”(Command Query Responsibility Segregation ),簡稱 CQRS。
  • CQRS(Command Query Responsibility Segregation,命令查詢職責分離)是一種架構模式,它的核心思想是將系統的**寫操作(命令)讀操作(查詢)**分離,使用不同的模型來處理。
  • 想象一下圖書館的管理方式:借書和還書(寫操作)由前臺工作人員處理,而查詢書籍位置或可用性(讀操作)則由咨詢臺負責。這種職責分離提高了效率,這正是CQRS的核心思想。

1.2 CQRS與CRUD的對比

  • 傳統CRUD(Create, Read, Update, Delete)架構中,讀寫操作使用同一個數據模型:
客戶端
服務層
同一數據模型
數據庫

而CQRS將讀寫分離:

客戶端
命令模型
查詢模型
寫數據庫
讀數據庫

1.3 為什么需要CQRS?

  • 讀寫負載不均衡:大多數系統讀操作遠多于寫操作
  • 性能優化:可以為讀寫分別優化
  • 簡化復雜性:避免單一模型同時滿足讀寫需求帶來的妥協
  • 擴展性:讀寫可以獨立擴展

二 CQRS深入:架構細節

2.1 基本架構組成

一個典型的CQRS系統包含以下組件:

  1. 命令端(Command Side)

    • 處理創建、更新、刪除等操作
    • 通過命令(Command)觸發
    • 產生領域事件(Domain Events)
  2. 查詢端(Query Side)

    • 處理數據查詢
    • 針對展示需求優化
    • 通常是非規范化的數據視圖
  3. 同步機制

    • 保持命令端和查詢端數據一致性
    • 可通過事件溯源(Event Sourcing)或定期同步實現

查詢端
同步機制
命令端
客戶端
定期同步
查詢處理器
DTO投影
事件處理器
讀數據庫
命令處理器
聚合根
領域事件
寫數據庫
命令模型
發送命令
查詢模型
發起查詢

2.2 數據流示意圖

客戶端 命令模型 寫模型存儲 讀模型存儲 查詢模型 發送命令(如"下訂單") 更新寫模型 確認更新 返回結果 異步更新讀模型 查詢訂單狀態 獲取數據 返回數據 返回查詢結果 客戶端 命令模型 寫模型存儲 讀模型存儲 查詢模型

三 CQRS實戰:電商訂單案例

通過一個電商訂單系統來具體理解CQRS的實現。

3.1 傳統CRUD方式的訂單處理

  • 在傳統方式中,我們可能會有一個Order類同時處理讀寫:
public class Order {private Long id;private String customerId;private List<OrderItem> items;private OrderStatus status;private Date createdDate;// 讀方法public BigDecimal calculateTotal() {return items.stream().map(i -> i.getPrice().multiply(i.getQuantity())).reduce(BigDecimal.ZERO, BigDecimal::add);}// 寫方法public void addItem(Product product, int quantity) {// 驗證邏輯...items.add(new OrderItem(product, quantity));}
}

這種方式隨著業務復雜化會變得難以維護。

3.2 CQRS方式的訂單系統

3.2.1 命令端實現

  • OrderCommandService.java (處理寫操作)
public class OrderCommandService {private final OrderRepository orderRepository;private final EventPublisher eventPublisher;@Transactionalpublic void createOrder(CreateOrderCommand command) {// 驗證業務規則if (command.getItems().isEmpty()) {throw new IllegalArgumentException("訂單不能為空");}// 創建聚合根Order order = new Order(command.getOrderId(),command.getCustomerId(),command.getItems());// 保存orderRepository.save(order);// 發布事件eventPublisher.publish(new OrderCreatedEvent(order.getId(),order.getCustomerId(),order.getItems(),order.getStatus()));}public void cancelOrder(CancelOrderCommand command) {// 類似實現...}
}

3.2.2 查詢端實現

  • OrderQueryService.java (處理讀操作)
public class OrderQueryService {private final OrderReadRepository readRepository;public OrderDTO getOrderById(String orderId) {return readRepository.findById(orderId).orElseThrow(() -> new OrderNotFoundException(orderId));}public List<OrderSummaryDTO> getOrdersByCustomer(String customerId) {return readRepository.findByCustomerId(customerId);}
}

3.2.3 讀模型DTO

public class OrderDTO {private String orderId;private String customerId;private List<OrderItemDTO> items;private String status;private BigDecimal totalAmount;private Date createdDate;// 僅包含簡單getter/setter
}public class OrderSummaryDTO {private String orderId;private String status;private BigDecimal totalAmount;private Date createdDate;private int itemCount;// 僅包含簡單getter/setter
}

3.3 數據同步實現

  • 使用領域事件同步讀寫模型:
@Component
public class OrderEventListener {private final OrderReadRepository readRepository;@EventListenerpublic void handleOrderCreated(OrderCreatedEvent event) {OrderDTO orderDTO = new OrderDTO();orderDTO.setOrderId(event.getOrderId());// 其他字段映射...readRepository.save(orderDTO);}@EventListenerpublic void handleOrderCancelled(OrderCancelledEvent event) {OrderDTO order = readRepository.findById(event.getOrderId()).get();order.setStatus("CANCELLED");readRepository.save(order);}
}

四 CQRS進階:高級話題

4.1 最終一致性處理

由于讀寫分離,CQRS系統通常是最終一致性的。處理方式包括:

  1. 事件驅動的更新:通過領域事件觸發讀模型更新
  2. 補償事務:當更新失敗時執行補償
  3. 版本控制:檢測和處理并發沖突

4.2 CQRS的適用場景

CQRS并非銀彈,適合以下場景:

  • 讀寫負載差異大的系統
  • 復雜領域模型,讀寫需求差異大
  • 需要高性能查詢的系統
  • 需要審計日志或歷史追蹤的系統

不適合的場景:

  • 簡單CRUD應用
  • 對實時一致性要求極高的系統
  • 開發資源有限的小型項目

五 CQRS實踐建議

5.1 實施步驟

  1. 從簡單開始:可以先在單個有界上下文(Bounded Context)中嘗試
  2. 明確邊界:清晰劃分命令和查詢的邊界
  3. 漸進式演進:從分離模型開始,逐步引入事件溯源等高級特性

5.2 常見陷阱與解決方案

陷阱解決方案
過度設計從實際需求出發,只在必要時引入CQRS
數據不一致實現健壯的事件處理機制,監控延遲
事件風暴使用事件溯源時合理設計事件粒度
開發復雜性提供充分的文檔和示例代碼

5.3 性能考量

  1. 讀模型優化

    • 使用非規范化設計
    • 針對查詢場景定制數據結構
    • 考慮使用專門的查詢數據庫(如Elasticsearch)
  2. 寫模型優化

    • 使用聚合根保證一致性邊界
    • 合理設計命令處理流程
    • 考慮批處理和異步處理

六 總結

CQRS是一種強大的架構模式,通過分離讀寫職責可以帶來諸多好處:

  1. 領域模型更清晰:命令端專注于業務規則,查詢端專注于展示需求
  2. 性能更優:可以針對讀寫分別優化和擴展
  3. 靈活性更高:可以輕松添加新的查詢而不影響命令處理

然而CQRS也帶來了額外的復雜性,應該根據項目實際需求謹慎采用。對于初學者,建議從一個小的、非核心的功能開始實踐,逐步積累經驗。

  • 記住,架構模式是工具而非目標,選擇適合你項目的最簡單有效的方案才是明智之舉。

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

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

相關文章

項目測試-接口測試

軟件測試的分類 軟件測試主要分硬件和軟件 硬件測試: cpu,內存條,顯卡...測試可以看得見摸得著的東西 軟件測試: web,app,小程序... 測試可以看得見摸不著的東西 web端 web端是在電腦上常常使用的, 也可以稱之為網站.(web端是B/S架構) web端的客戶端是任何一個訪問這個網…

相機的光圈

光圈&#xff08;Aperture&#xff09;是鏡頭中一個控制光線進入相機的開口&#xff0c;它在攝影中起著至關重要的作用。光圈的大小決定了進入相機傳感器的光線數量&#xff0c;并影響曝光、景深、以及拍攝效果。光圈參數通常用f/值&#xff08;光圈值&#xff09;來表示&#…

HarmonyOS NEXT倉頡開發語言實戰案例:小而美的旅行App

大家周末好&#xff0c;本文分享一個小而美的旅行app首頁&#xff0c;效果圖如下&#xff1a; 很顯然這個頁面還是使用List容器&#xff0c;頁面兩側有統一的邊距&#xff0c;我們可以在List容器統一設置&#xff1a; List(space:20){ } .padding(left:14,right:14,top:62) .w…

Python銀行管理系統01升級(適合初學者)

目錄 框架如下: 1. Account類 - 賬戶數據模型 2. Bank類 - 銀行業務邏輯 3. BankApp類 - 圖形用戶界面 關鍵概念解析(適合初學者) 1. 面向對象編程(OOP)概念 2. Tkinter GUI編程基礎 3. 數據持久化 4. 輸入驗證 學習建議 系統功能概覽 完整代碼: 在Python銀行…

華為防火墻雙向NAT實驗

如圖所示&#xff0c; 企業內網有一臺Server2&#xff0c;通過在FW1上配置nat server&#xff0c;將Server2的www端口映射到了公網&#xff1b; 實驗環境中&#xff0c;內網和外網都使用外網的server1提供的DNS服務&#xff0c;在DNS服務器上添加A記錄&#xff0c;www.baidu.c…

前端路由的基石:深度剖析 Hash 與 History 模式的本質差異與實戰抉擇

在單頁面應用&#xff08;SPA&#xff09;統治現代Web開發的今天&#xff0c;前端路由已成為構建流暢用戶體驗的核心技術。而hash和history作為兩種主流實現方案&#xff0c;其設計理念和技術細節的差異直接影響著應用架構的選擇。本文將深入解析二者的技術本質&#xff0c;通過…

微機系統 - 緒論

緒論: 一:微處理器,微型計算機和微型計算機系統: 分類: 按照系統結構和基本工作原理.計算機分為5大部分:運算器,控制器,存儲器,輸入設備,輸出設備 按照體積,性能和價格分5類:巨型機,大型機,中型機,小型機,微型計算機(單板機,單片機) 微型計算機的特點:集成度高,體積小,重量輕…

基于Java+Springboot的寵物健康咨詢系統

源碼編號&#xff1a;S564 源碼名稱&#xff1a;基于Springboot的寵物健康咨詢系統 用戶類型&#xff1a;多角色&#xff0c;用戶、顧問、管理員 數據庫表數量&#xff1a;12 張表 主要技術&#xff1a;Java、Vue、ElementUl 、SpringBoot、Maven 運行環境&#xff1a;Win…

SpringBoot+MySQL寵物貓店管理系統

概述 基于SpringBootMySQL開發的寵物貓店管理系統完整源碼。該系統功能完善&#xff0c;包含前后臺完整功能模塊&#xff0c;代碼規范易于二次開發&#xff0c;是學習SpringBoot項目實戰的優秀范例。 主要內容 前臺功能展示 系統前臺設計簡潔實用&#xff0c;主要包含以下核…

UE5 - 制作《塞爾達傳說》中林克的技能 - 16 - 遙控炸彈(一)

讓我們繼續《塞爾達傳說》中林克技能的制作&#xff01;&#xff01;&#xff01; 本章節的核心目標&#xff1a;素材導入與遙控炸彈的外觀 先讓我們看一下完成后的效果&#xff1a; 基本流程&#xff1a;素材準備->C類開發->藍圖配置->場景部署 1.素材準備&#xff1…

HTTP中常見的Content-Type

Content-Type&#xff0c;也稱為互聯網媒體類型或MIME類型&#xff0c;是HTTP協議中的一個頭部字段&#xff0c;用于指定處理請求和響應中的媒體類型信息。它告訴服務器如何處理請求的數據&#xff0c;同時也指導客戶端&#xff08;通常是瀏覽器&#xff09;如何解析響應的數據…

Android11 wifi開啟源碼分析

目錄 一、APP層源碼分析 1.1、尋找頁面activity 1.2、尋找頁面開關按鈕布局 二&#xff0c;framework層代碼分析 2.1 開啟wifi入口 2.2 WiFiNative 三&#xff0c;HAL層代碼分析 這段時間擼了WIFI開啟流程源碼&#xff0c;本著前人栽樹后人乘涼的原則&#xff0c;有志于…

R語言使用nonrandom包進行傾向評分匹配

傾向評分匹配&#xff08;Propensity Score Matching&#xff0c;簡稱PSM&#xff09;是一種統計學方法&#xff0c;用于處理觀察研究&#xff08;Observational Study&#xff09;的數據&#xff0c;在SCI文章中應用非常廣泛。在觀察研究中&#xff0c;由于種種原因&#xff0…

LeetCode Hot 100 找到字符串中所有字母異位詞

給定兩個字符串 s 和 p&#xff0c;找到 s 中所有 p 的 異位詞 的子串&#xff0c;返回這些子串的起始索引。不考慮答案輸出的順序。 示例 1: 輸入: s "cbaebabacd", p "abc" 輸出: [0,6] 解釋: 起始索引等于 0 的子串是 "cba", 它是 "a…

關于廬山派多視頻層(layer)和bind_layer的應用

嘉立創分了適配層和OSD&#xff08;我稱它為圖片層&#xff09;顧名思義&#xff0c;一個是能顯示視頻流到LCD屏幕&#xff0c;一個是只能顯示照片&#xff0c;也就是你可以對不同層進行操作而不影響其他層&#xff0c;解決的場景就是用于你畫了一個正方形在照片上&#xff0c;…

多傳感器標定簡介

目錄 標定內容及方法 雷達內參標定 IMU內參標定 編碼器內參標定 相機內參標定 雷達和相機外參標定 多雷達外參標定 手眼標定 融合中標定 總結 連續時間 標定內容及方法 雷達內參標定 1) 目的 由于安裝原因&#xff0c;線束之間的夾角和設計不一致&#xff0c;會導致…

day46/60

浙大疏錦行 DAY 46 通道注意力(SE注意力) 知識點回顧&#xff1a; 不同CNN層的特征圖&#xff1a;不同通道的特征圖什么是注意力&#xff1a;注意力家族&#xff0c;類似于動物園&#xff0c;都是不同的模塊&#xff0c;好不好試了才知道。通道注意力&#xff1a;模型的定義和插…

提升創作效率:輕松調用固定素材與模板

日常工作和生活中&#xff0c;我們經常需要復制粘貼不同類型的數據&#xff0c;如文本、圖片、文件等。使用剪切板管理工具可以快速訪問之前復制的內容&#xff0c;而無需反復切換應用進行復制操作。 這款綠色便攜版應用&#xff0c;無需安裝&#xff0c;雙擊即開&#xff0c;…

【C++】組合模式

目錄 一、模式核心概念與結構二、C 實現示例&#xff1a;文件系統三、組合模式的關鍵特性四、應用場景五、組合模式與其他設計模式的關系六、C 標準庫中的組合模式應用七、優缺點分析八、實戰案例&#xff1a;圖形編輯器九、實現注意事項如果這篇文章對你有所幫助&#xff0c;渴…

C++包管理工具:conan2持續集成 (CI) 教程

1.持續集成 (CI) ? 這是一個高級主題&#xff0c;需要具備 Conan 的基礎知識。請先閱讀并練習用戶教程。本節面向設計和實施涉及 Conan 包的生產 CI 管道的 DevOps 和構建工程師。如果不是這種情況&#xff0c;您可以跳過本節。 持續集成 (CI) 對不同用戶和組織有不同的含義…