命令和事件有什麼區別? - Oskar

banq發表於2021-12-10

命令代表意圖:它針對特定的受眾。當你問“把鹽遞給我”時,它可以是你的朋友。它可以是一個應用服務和請求,意圖是“新增使用者”或“將訂單狀態更改為已確認”。所以命令的傳送者必須知道接收者並期望請求被執行。當然,接收者可能會拒絕這樣做,因為在請求處理期間不向我們傳遞鹽或丟擲異常。

事件代表了過去的事實:它攜帶有關已完成某事的資訊。所見所聞,不可不察。按照我們的例子“使用者新增”,“訂單狀態更改為確認”是過去的事實。我們不會將事件定向到特定的接收者,我們會廣播資訊。這就像在聚會上講故事一樣。我們希望有人傾聽我們,但我們可能很快就會意識到沒有人在關注。
 

事件和命令有什麼共同點?
兩者都是訊息。它們傳達特定的資訊;關於做某事的意圖的命令,或關於發生的事實的事件。從計算機的角度來看,它們沒有什麼不同。只有業務邏輯和訊息的解釋才能發現事件和命令的區別。
通常假設命令是同步的,而事件是非同步的。我們通常透過例如 Rest API 傳送命令,透過佇列(記憶體中、RabbitMQ、Kafka 等)傳送事件。這種區別來自習慣。當我們傳送命令時,我們想立即知道它是否已經完成。通常,我們希望在操作失敗時進行替代方案或錯誤處理。同樣,我們通常假設最好立即停止該過程,例如購買電影票,而不是等待並重新整理並檢視它是否有效。
這是有道理的,但並不總是那麼明顯。例如,銀行轉賬:當我們成功時,它不會立即發生。我們得等一會兒。在 Internet 上進行購買時也是如此。下訂單和付款不會立即完成整個過程。它還是要發貨,發票開具等等。這是一個非同步過程,所以我們的命令的結果也可能是非同步的。
 

微服務和分散式系統增加了額外的複雜性。
傳統上,發件人需要知道該告訴誰來接管其餘的工作。使用排隊/流系統逆轉服務的依賴性。傳送者將訊息釋出到統一頻道,接收者訂閱並等待。當我們購買電影票時,必須生成收據,必須預訂座位,並透過電子郵件通知使用者並在網站上顯示。所有這些都可以拆分為單獨的工作流程步驟。哪個應該由命令觸發,哪個應該由事件觸發?
我們可以使用啟發式:當接收者有權拒絕請求時,我們傳送命令,當接收者接受時傳送事件。如果我們要新增使用者,系統可能會拒絕我們使用現有使用者名稱。金融服務可以拒絕預訂服務生成收據嗎?如果預訂被確認,轉賬已經完成,那麼財務模組應該接受它。那麼,這是一個事件嗎?
理論上,是的,如果沒有驗證規則並且事件發生了,另一個系統應該接受它並執行邏輯。即使金融服務暫時不可用,類似 Kafka 的流媒體系統也應保證交付。我們在程式碼中有錯誤?然後我們必須修復它們。
到這裡,理論結束,實踐開始。我們的客戶不在乎錯誤是否是我們知道並在不久的將來修復的錯誤?客戶想要開展業務並運營。
當然,在訊息佇列教程中,你可以找到這樣的建議:“有一個死信佇列/毒訊息佇列,未處理的訊息將被放置在那裡。你可以在那裡檢查並做出反應”。這很酷,但有多少人在監視它?即使他們這樣做了,他們能多快做出反應?假設我們收集指標、傳送警報、快速支援響應並向程式設計師報告“票”。他們會多快修復它?我們可以透過預先設計並新增補償操作(例如,如果預訂確認事件丟失時在 UI 中生成發票的按鈕)來簡化這一點。
有時事實證明,一個給定的事件總是隻有一個接收者。更重要的是,我們希望它始終得到處理。它仍然是一個事件嗎?值得考慮的是,在名稱為“預訂已確認”的事件下,我們是否不傳送“發出收據”命令。當業務流程至關重要時,我們可能更願意快速失敗並立即獲得結果。對於這種情況,值得考慮是否執行非同步命令比執行非同步事件更好。
 

命令和事件之間的區別可能並不像看起來那麼簡單。我們需要考慮很多因素:

  • 是有意做某事還是記錄的事實?
  • 是非同步操作還是同步操作?
  • 它可以有多個收件人嗎?
  • 它是關鍵業務嗎?
  • 還有很多


如果有人問你“這是一個命令還是一個事件”,不要害怕說“這取決於”,然後開始著手理解業務邏輯。

相關文章