大多數(shù)編程語言中if語句主要有兩個作用:驗證輸入以保護領域免受錯誤數(shù)據(jù)的影響,以及處理領域內業(yè)務邏輯。但是,Udi Dahan最近在阿姆斯特丹DDD歐洲會議上的發(fā)言中指出,我們一般很少從業(yè)務或領域角度管理使用if語句處理邏輯的風險。
我們在線購物時會瀏覽不同的商品,并仔細閱讀其中一些商品的詳細信息。當找到想要購買的商品并將其添加到購物車中時,我們也從交互的查詢功能轉移到命令功能。對任何類型的命令,Dahan認為我們應該問的重要問題之一是,什么因素會導致該命令失效。他還強調了我們必須區(qū)分技術失效(例如網(wǎng)絡服務器崩潰或者反序列化錯誤,這應該由基礎架構解決)和邏輯失效(比如將已經從產品目錄中刪除的商品添加到購物籃)。
Dahan與客戶合作的一個常見案例就是檢查是否有項目被刪除。對他來說,處理已刪除項目這類問題的一個步驟是區(qū)分私有數(shù)據(jù)和公共數(shù)據(jù),并與內容管理系統(tǒng)進行比較。在內容管理系統(tǒng)中,你可以編輯頁面和內容,最后通過按“發(fā)布”鍵公開信息。在處理私有數(shù)據(jù)方面,用戶可以隨意添加、更新或刪除項目,直到最后滿意了再公開。
在處理公共數(shù)據(jù)方面,我們應該更多地用業(yè)務邏輯來替換刪除和檢查刪除,應該更仔細地斟酌為什么要刪除某個項目。以某個不再出售的產品為例,或許更好的方法是創(chuàng)建一個特定的命令,將該產品標記為不出售,而不是使用某種形式的暫時或真正的刪除。
這種解決方案的潛在問題是竟態(tài)條件(race condition)。即顧客將商品添加到購物籃后該商品被標記為不出售,之后當客戶想要結賬時已經無法購買這項商品了。對于Dahan來說,以這種方式看待問題的視角是非常狹窄的、以數(shù)據(jù)為中心且局限于某個時間點。相反地,他將這個問題描述為多個參與者同時在同一個對象上操作的典型場景。
對于在協(xié)作領域多個參與者同時對相同的數(shù)據(jù)進行操作的情況,Dahan認為我們應該開始考慮使用CQRS。當CQRS應用于領域時,他建議使其盡可能簡單化,并設計好解決方案,以保證經過驗證后應用于領域邏輯的命令幾乎不會失效。這意味著在處理命令時,我們必須準備好在競態(tài)條件下失敗卻仍然可以滿足整體業(yè)務目標。通常這從商業(yè)角度來說能達到最終的一致性,但不是技術上的一致性(如讀取模型最終與寫入模型同步)。舉例來說就是客戶把某個不出售的商品添加到購物籃中。
這個問題的一個解決方案是使用幕后長期運行過程,當用戶不再操作購物籃或者超時時,從購物籃移除已停止銷售的商品。最終,該商品被從所有購物籃中移除,因此不再銷售。
當我們查看系統(tǒng)時可能會發(fā)現(xiàn)許多以某種形式檢查狀態(tài)的if語句,對于每一個if語句,我們都應該搞清楚是否有其他參與者可能改變該if語句所檢查的狀態(tài)。這樣我們可以找到潛在的協(xié)作情形和對長期運行過程的需求。不過Dahan指出,我們要注意不要使用太多的長期運行過程,它并不是萬能的。
查看英文原文:The Dangers of If Statements in Domain Logic