關於對象之間通信的一點思考


經典的DDD的告訴我們如果一個領域概念是一個跨多個聚合的動作,比如轉帳,那么就應該用領域服務來實現這樣的業務概念。領域服務的輸入和輸出參數都是聚合根,領域服務內部按照業務邏輯規定的執行順序,按照面向過程的方式,逐個調用相關聚合根的相關方法完成整個業務操作。這種方式的優點是:1)清晰的表達和封裝了業務邏輯;2)代碼清晰,容易理解,代碼可讀性強;缺點:1)基本的OO思想告訴我們,對象與對象之間應該是通過發送消息和接收消息的方式來通信的。但是通過前面這種方式,對象之間不再像我們想的那樣會通過發送消息和接收消息來相互協作了,而都是被動的被一個第三方協調者服務對象來統一協調,這其實是一種面向過程的思維,而非基於消息通信的面向對象思維;2)這種通過領域服務的方式只強調了對象的個體行為(即對象只有改變自己狀態的行為),而沒有注重對象之間的交互行為,對象應該還有能力發送消息給其他對象或者響應其他對象發送的消息;
 
那么為了能讓對象之間有交互行為,能相互發送消息和接受消息,該如何做呢?一般來說有兩種方法:1)方法調用,A對象調用B對象的方法,這種方法最常用最直接,但是缺點是會導致A依賴於B。這個問題我們往往會通過面向接口編程在一定程度上消除這種依賴關系;2)事件消息模式,生產者-消費者模式,即采用觸發事件響應事件的模式;通過這種方式,A對象就不是直接調用B對象的方法了,而是觸發一個事件,然后B對象去響應這個事件,或者如果采用消息總線的方式的話,就是A對象通知消息總線發布某個事件,然后消息總線發布這個事件,然后B對象響應這個事件。通過這樣的通信方式,巧妙的將A,B兩個對象的關系進行了解耦,本來A直接依賴B(A->B)的情況變成了A與B相互獨立,變成了這種方式:A->消息<-B。
 
CQRS & Event Sourcing架構采用的通信方式。首先,在這種方式下,聚合與聚合之間的關系通過ID表示,而不是通過對象引用的方式來表達對象關聯。這就讓我們不能直接通過簡單方便的對象導航的方式定位到關聯對象並調用其方法了。那么這種方式下,對象之間如何通信呢?就是采用上面提到的基於事件消息的通信模式。CQRS架構中Command端的大致執行流程是:UI發送Command到CommandService,CommandService根據傳入的Command調用相關的Command Handler,Command Handler獲取相關的聚合執行相關方法,聚合執行方法后觸發事件,消息總線發布該事件到外部,事件響應者(Event Handler)響應事件。關於最后一步的實現還有一些目前不確定的方案,目前采用一般兩種方式:1)Event Handler生成一個新的Command然后發送給Command Service;2)Event Handler直接取出目標聚合根,然后調用其方法;另外,一般兩個聚合之間采用異步的方式通信,所以我們會集成NServiceBus等異步消息通信框架實現消息的傳遞;
 
關於對象之間通過直接方法調用來交互,和通過發送消息來交互的一點思考:
1)對象A直接調用對象B的某個方法,實現交互;直接方法調用本質上也是屬於一種特殊的發送與接受消息,它把發送消息和接收消息合並為一個動作完成;方法調用方和被調用方被緊密耦合在一起;因為發送消息和接收消息是在一個動作內完成,所以無法做到消息的異步發送和接收;
2)對象A生成消息->將消息通知給一個消息處理器(如NServiceBus,EventBus之類)->消息處理器通過同步或異步的方式將消息傳遞給接收者;這種方式是通過將消息發送和消息接收拆分為兩個過程,通過一個中間者來控制消息是同步還是異步發送;在消息通信的靈活性方面比較有優勢,但是也帶來了一定的復雜度。但是復雜度一般可以由框架封裝,消息的發送方和接收方仍然可以做到比較簡單;
 
最后,在說一下關於事件與消息的關系的論述:
事件和消息可以說是從不同方面描述的同一個東西,消息是事件發生后產物,消息發送必須有發送事件發生才能實現。每次事件只發送一次消息,事件和消息是一對一的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM