背景:
作為一個研發,我們工作中都會處理面臨下面這些困惑:
- 又加需求,一個方法本來就處理了 300 行,現在又加 50 行。
- 狀態邏輯太多了,產品第 2 期又加了一個邏輯,程式碼結構要調整,很頭疼。
- 每個人都在吐槽,業務研發在工作中處理最多的就是 if else,好不容易寫個 switch 都能給同事吹一週。
以上三個場景應該是日常需求迭代優化中面臨最多的場景了,作為一個自稱編碼水平較高的人,總結了以下三個真實的場景,給出一些可選的方案。
第一板斧:抽象事件,驅動業務
核心
梳理產品邏輯中的主流程節點,整理節點所需要的依賴資料已經節點觸發後對應的業務邏輯。
類比訊息佇列,也是不同的業務方訂閱自己的事件源,進行不同的處理。不同點在於一個是分散式,一個是本文描述單機業務處理場景。
實際例子
舉一個使用者註冊之後的場景,需要:
- 發簡訊
- 發優惠券
如果使用者註冊成功之後,直接發了mq訊息,那麼使用者系統和券系統分別訂閱這個訊息進行處理。不過這裡討論的是在一個專案模組中處理完所有相關的邏輯。
程式碼將在 UserRegistered()
中一步一步去處理邏輯,之後需求又加入了 初始化A資料和初始化B資料 兩個需求,實現也會落到這個方法之後,最後整個程式碼會越來越臃腫。
接下來用事件訂閱模型去化解這個點,非常實用,一點都不華麗,程式碼也很好讀懂。
最後對應到程式程式碼可能是這樣的:
# 事件註冊
Event::register(UserRegistered::class, [
SendSms::class,
SendCoupon::class,
InitSystemA::class,
InitSystemB::class,
]);
# 業務呼叫
handleRegistered($user) {
$event = new UserRegistered($user);
$event->fire();
}
後面的迭代維護中,只要主流程不發生變化,那麼相應的邏輯只需要去增加訂閱者去實現。
第二板斧:有限狀態機,定義流程
在業務邏輯資料處理這一層,很多的業務場景都與資料扭轉狀態有關,並且最後會有相應的資料實體相對映。比如我們常見的:
- 各種商品訂單(天貓,淘寶,外賣)
- 工作流(審批,工單處理)
這類需求的特點是,讀寫場景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個欄位來表達我們的資料模型。
拿一個訂單舉例子,我們有如上訂單特徵。不可避免的,每一個業務場景,每一個邏輯,產品邏輯都可能有自己的配置和相應的處理流程,且這些邏輯都是業務迭代優化的重災區,比如:
- 江浙滬地區包郵
- 某一批固定的城市需要打8.8折
- 雨天調價格
- 法定節假日打烊不服務
- vip身份的使用者展示文案特殊處理
每一個開發同學都曾被這些邏輯折磨的異常痛苦,這裡給出一個抽象的方案,最終每一個訂單特徵都會落到具體的業務處理類,所有的類都實現該業務場景的interface,主流程只需要構造 n元組然後獲取到相應的 interface 之後進行呼叫。
n元組的概念其實早已經滲透到了開發中的每一個角落,我們需要做的事情就是,在業務開發的時候真正的去思考這一層資料模型,然後加已運用,最後的程式碼一定不那麼 if else。
總結
以上三板斧在業務中使用可以很輕,也可以很重。這就意味著我們能自己寫一個簡單夠用的(對於你完全瞭解成長有限的業務場景),或者找一個star多且在維護的開源方案(對於有潛力,未來大有可為的業務)來代替。同時,這些編碼套路在各種場景下都能非常靈活的組合,比如:
- 訂單狀態更新後觸發事件(狀態機+事件訂閱)
- 不同業務線,狀態機配置,初始化放鬆不同(n元組+狀態機)
不得不提到的一點,在漫長的業務迭代中,產品文件會越來越缺失,最終只有通過程式碼才能瞭解線上的真正邏輯(有時間程式碼過於複雜,可能就沒有人知道線上的具體情況了),集中配置,統一維護的意義之一就在於此。相信做過複雜歷史系統的交接或重構的同學對這一點都深有體會。
參考
最後歡迎大家關注我的公眾號,我給自己定個每個目標達成之後都會有實體書贈送抽獎活動。
本作品採用《CC 協議》,轉載必須註明作者和本文連結