業務邏輯開發套路的三板斧

轉自:代碼這件大事

背景:

作爲一個研發,我們工作中都會處理面臨下面這些困惑:

  1. 又加需求,一個方法本來就處理了 300 行,現在又加 50 行。

  2. 狀態邏輯太多了,產品第 2 期又加了一個邏輯,代碼結構要調整,很頭疼。

  3. 每個人都在吐槽,業務研發在工作中處理最多的就是 if else,好不容易寫個 switch 都能給同事吹一週。以上三個場景應該是日常需求迭代優化中面臨最多的場景了,作爲一個自稱編碼水平較高的人,總結了以下三個真實的場景,給出一些可選的方案。

第一板斧:抽象事件,驅動業務

核心

梳理產品邏輯中的主流程節點,整理節點所需要的依賴數據已經節點觸發後對應的業務邏輯。類比消息隊列,也是不同的業務方訂閱自己的事件源,進行不同的處理。不同點在於一個是分佈式,一個是本文描述單機業務處理場景。

實際例子

舉一個用戶註冊之後的場景,需要:

  1. 發短信

  2. 發優惠券 如果用戶註冊成功之後,直接發了mq消息,那麼用戶系統和券系統分別訂閱這個消息進行處理。不過這裏討論的是在一個項目模塊中處理完所有相關的邏輯

代碼將在 UserRegistered() 中一步一步去處理邏輯,之後需求又加入了 初始化A數據初始化B數據 兩個需求,實現也會落到這個方法之後,最後整個代碼會越來越臃腫。

接下來用事件訂閱模型去化解這個點,非常實用,一點都不華麗,代碼也很好讀懂。

最後對應到程序代碼可能是這樣的:

# 事件註冊
Event::register(UserRegistered::class, [
    SendSms::class,
    SendCoupon::class,
    InitSystemA::class,
    InitSystemB::class,
]);

# 業務調用
handleRegistered($user) {
    $event = new UserRegistered($user);
    $event->fire();
}

後面的迭代維護中,只要主流程不發生變化,那麼相應的邏輯只需要去增加訂閱者去實現。

第二板斧:有限狀態機,定義流程

在業務邏輯數據處理這一層,很多的業務場景都與數據扭轉狀態有關,並且最後會有相應的數據實體相映射。比如我們常見的:

  1. 各種商品訂單(天貓,淘寶,外賣)

  2. 工作流(審批,工單處理)

這類需求的特點是,讀寫場景QPS不高,對數據的準確一致性要求非常高。我們底層一般直接存儲到數據庫,之上加一層簡單的數據緩存就能處理。

面臨的主要問題是,狀態太多難以維護,應該還會出現狀態的調整比如特殊場景下的狀態A到狀態Z的扭轉。 

不過業內早已給出了比較通用的解決方案,有限狀態機。下面我們列舉一個簡單的訂單狀態扭轉邏輯:

fsm := fsm.NewFSM(
    "created",
    fsm.Events{
        {Name: "pay", Src: []string{"created"}, Dst: "paid"},  //支付
        {Name: "cancel", Src: []string{"created"}, Dst: "closed"}, //關閉
    },
    fsm.Callbacks{
        "after_pay": func(e *fsm.Event) { /* 支付成功調用 */ },
    },
)

如果設計到狀態相關的調整,在狀態機定義的地方去修改就可以解決問題。和事件訂閱非常相識,也是集中維護,統一管理。

第三板斧:n元組配置,組合輸出

軟件工程沒有銀彈   --布魯克斯

軟件開發中我們遇到的一個一個需求都是不可預測的,我們確實很難找到一種終極的解決方案。不過本文中的討論侷限在業務邏輯開發的開發套路。那麼,n元組這個簡單的概念可能算得上一顆銀彈。不管業務邏輯有多複雜,在理論上我們都能抽象出n個字段來表達我們的數據模型。

拿一個訂單舉例子,我們有如上訂單特徵。不可避免的,每一個業務場景,每一個邏輯,產品邏輯都可能有自己的配置和相應的處理流程,且這些邏輯都是業務迭代優化的重災區,比如:

  1. 江浙滬地區包郵

  2. 某一批固定的城市需要打8.8折

  3. 雨天調價格

  4. 法定節假日打烊不服務

  5. vip身份的用戶展示文案特殊處理

每一個開發同學都曾被這些邏輯折磨的異常痛苦,這裏給出一個抽象的方案,最終每一個訂單特徵都會落到具體的業務處理類,所有的類都實現該業務場景的 interface,主流程只需要構造 n元組然後獲取到相應的 interface 之後進行調用。

n元組的概念其實早已經滲透到了開發中的每一個角落,我們需要做的事情就是,在業務開發的時候真正的去思考這一層數據模型,然後加以運用,最後的代碼一定不那麼 if else。

總結

以上三板斧在業務中使用可以很輕,也可以很重。這就意味着我們能自己寫一個簡單夠用的(對於你完全瞭解成長有限的業務場景),或者找一個star多且在維護的開源方案(對於有潛力,未來大有可爲的業務)來代替。同時,這些編碼套路在各種場景下都能非常靈活的組合,比如:

  1. 訂單狀態更新後觸發事件(狀態機+事件訂閱)

  2. 不同業務線,狀態機配置,初始化放鬆不同(n元組+狀態機)

不得不提到的一點,在漫長的業務迭代中,產品文檔會越來越缺失,最終只有通過代碼才能瞭解線上的真正邏輯(有時間代碼過於複雜,可能就沒有人知道線上的具體情況了),集中配置,統一維護的意義之一就在於此。相信做過復雜歷史系統的交接或重構的同學對這一點都深有體會。

參考

[有限狀態機](https://zh.wikipedia.org/zh-hans/%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA)

[go fsm](https://github.com/looplab/fsm)

熱文推薦

Spring Boot 與微服務從0到1的實踐

我在外包公司做增刪改查有前途麼?

那天晚上和@FeignClient註解的深度交流

如有收穫,點個在看,誠摯感謝

發佈了253 篇原創文章 · 獲贊 101 · 訪問量 55萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章