業務建模:CQRS應用場景

clonalman發表於2012-08-29

分析了做過的一些專案(基於經典DDD),覺得應用CQRS的場景還是蠻多的,

特別是當出現模組之間出現相互依賴的時候,我這裡說的應用場景不是為了保證查詢資料的一致性,

而是由領域出發自然而然的過程。

舉一個例子:ERP中的物流或供應鏈管理會有銷售模組、採購模組、庫存模組、財務模組等,每個模組會有不同的人來負責。

 

就以“銷售訂單”來舉這個例子吧;

 

按照DDD設計,銷售人員需要知道當前訂單庫存的裝箱出貨情況、財務的開票付款情況,

所以設計上銷售訂單(在庫存上)單向導航的裝箱單集合、出庫交貨單集合(多次裝箱、多次出貨)、

(在財務上)單向導航到收款單集合與發票集合(多次收款、多次開票)

 

如果銷售、庫存、財務分開模組設計,銷售模組與庫存模組和財務模組必然是存在依賴關係,

銷售模組依賴於庫存模組和財務模組,而庫存模組和財務模組不能依賴銷售模組,否則會出現交叉引用。

 

現在我想要查詢,缺貨的訂單、已裝箱未出庫的訂單、部分收款已出庫、已開票已出庫的訂單:

把所有的訂單明細與裝箱單明細、出貨單明細、收款單明細、發票明細進行數量、金額比較得過濾出訂單,

說到這裡有人可能會拍磚了,傻啊,不會在訂單上加個庫存狀態屬性、財務狀態屬性,(有上生產模組的話,再加個生產模組屬性)

根據狀態來過濾,看上去似乎很美,但問題是誰來更新這些狀態?我們模仿一下DDD用一個對話來描述吧:

 

甲: 銷售人員?

乙: 銷售人員很閒的話可能會去幹(老總肯定覺得他真閒得沒事幹了),否則他們更願意看到庫存、財務作業結果。

甲: 庫存人員裝箱出貨作業完了通知銷售人員一下行了,財務有筆款到帳了也通知一下,不就行了。(不通知,銷售人員打電話去客戶那裡問問)

乙: 問題來了,庫存、財務怎麼知道這是哪個訂單對應這個出貨與款項了。

甲: 這些單據是根據訂單自動生成,上面都有一個訂單的訂單號,可以根據訂單號找到訂單更新一下狀態,不就好了。

乙: 你怎麼知道出庫單上關聯的那個號碼是訂單號,它也可能是採購退貨單的單號啊?

甲: 我們的編號都有規則訂單號是以SO開頭的,採購退貨單號是以RP開頭的,還有一個備註的屬性,一目瞭然。

乙: 你是知道,但你的電腦它知道嗎?(這問題問得很傻很天真)

甲: 我都參與了系統的開發,絕對是按照DDD規範來做,基礎設施層、領域層、應用層、表現層,多清楚啊。

乙: 你這個判斷關聯號碼的是否是訂單號的規則是不是應該寫在領域層的庫存模組裡?是屬於領域的一部分?

甲:當然

乙: 那裡怎麼獲得訂單並改變它的屬性狀態的?

甲:是通過訂單的服務介面,它是一個領域服務

乙: 這個訂單服務介面應該在訂單模組了對吧?

甲:當然

乙:銷售模組賴於庫存模組和財務模組,處於上層,你怎麼獲得對訂單服務藉口的引用?

甲:(開啟程式碼……)哦,原來我們把這個判斷號碼的判斷規則寫在了應用層,但它應該屬於業務的一部分的,怎麼把它移到領域層上去了?

乙:你的庫存裝箱、出貨有日誌(History)吧,History一個事件集合記錄作業過程,

可裝箱單或出庫交貨確認完成時產生髮布PickingEvent或DeliveryEvent等

銷售訂單或採購退貨單訂閱事件,讓他們自己去判斷這個單號是否屬於自己,修不修該狀態就是他們的事,跟庫存沒有關係。

當然在得在這個事件中帶有它們感興趣的資訊(直接把這個聚合物件給他們也可以)

甲:領域事件出現了,那就可以使用CQRS :)

 

--------------------------------------------------------------------------------------

 

以上是我杜撰的一個過程,銷售模組也不一定要這樣設計,與庫存模組、財務模組是可以解偶的,

解偶的話他們之間的聯絡,也是需要一個協調者或一個事件。

當然了,部分業務邏輯放在應用層,系統也能撮和著用,功能上沒問題,只有完美主義者才會較真的。

 

其實,當初碰到這個問題,之前我的處理方案很土

(庫存模組定義一個Service介面,在訂單模組引用實現並注入容器,庫存確認完成的時候獲得Service介面執行,

跟領域事件差不多,實現介面例項算是訂閱,通過Ioc容器來做釋出)

 

這是一個地球人都能明白的例子,從純業務角度來描述,涉及DDD分層、領域事件、CQRS,希望能對DDD理解有所幫助

個人觀點是,領域事件的出現是自然而然的,CQRS應作為一個具體特定場景解決方案。

相關文章