聊聊多版本業務模型設計
本文轉載自微信公眾號「編了個程」,作者Yasin x 。轉載本文請聯編了個程公眾號。
最近業務上用到比較多的多版本場景。這里總結一下多版本業務模型設計的思路。
多版本需求梳理
先梳理一下多版本的一般訴求:
- 同一個數據經過多次編輯后,會產生多個版本,其中歷史版本不能刪除掉,因為可能有上下游在使用;
- 多版本通常用于配置中,最新一個版本的配置通常可以多次修改、測試,確定后再發布;
- 已經發布的歷史版本不能隨便修改,因為有數據在使用;
- 在消費側,一般默認是使用最新已發布的版本;
- 多版本可能會有發布審批、與上一個版本的diff等需求場景;
多版本狀態機設計
一個多版本的業務模型,通常會有以下的狀態機。其中“廢棄”不是必須的,回滾操作也不是必須的(回滾操作會給代碼和表設計帶來很大的復雜性),發布中間可能會有發布中、審批中等狀態。
草稿可以在原版本編輯,但已發布的數據再編輯,就會生成一個新版本的草稿。
有時候也會有下線操作,這個時候所有版本的狀態就會被改為“已下線”。
多版本表設計
對于多版本而言,你需要有一個唯一標識這個業務數據的字段,可以叫id?或者code。
同時,需要一個字段來標識版本,這個版本建議是一個遞增的數字,叫version?。有些業務期望版本是業務輸入的,或者有一個版本說明的概念,那可以新增一個字段叫version_desc。
我們可以把唯一標識和版本拼接起來,作為這個數據在這個版本的唯一鍵,可以叫code_version?。通常是拼接成一個字符串,中間用某種特定的分隔符來區分,比如#?。那code_version?可能就長這樣:A12334#3?。這里就要求code?里面不能有分隔符#,不然代碼邏輯處理起來就比較麻煩。
這里說一下這個拼接字段的必要性,因為上下游往往會存code + version。那上下游在列表查詢等場景來查詢數據的時候,如果沒有這個字段,只能循環一個個查,不能用where批量查詢。
另外一個必要的字段就是status來標識當前版本的狀態。
還有一個非必須的字段is_last_version?,用來標識當前這條數據是不是它的最新版本,無論是草稿態還是已發布還是已廢棄,它都會變成true。這里在待會兒下文的查詢要點中會解釋它的用處。如果不用這個字段也能查,但是需要group by order,整體查詢語句麻煩,效率低。在寫的時候多維護一下這個數據,會讓查詢的時候方便很多。
其它的都是審計字段了。最終的建表語句可能長這樣:
生產端和消費端的查詢要點
生產端就是配置數據的地方,消費端就是使用的地方。生產端和消費端有一些區別,生產端往往要看最新的版本,包括草稿等狀態,還要能修改。而消費端一般只用最新已發布的版本。
生產端
生產端的查詢,可以按照is_last_version為true來過濾,這樣就只查每一個code的最新版本的數據。
同時,每個code也應該返回一個version?列表,是這個數據code_version的集合,以便用戶查看和跳轉歷史版本。
生產端的寫入,需要維護好狀態、版本、is_last_version等字段。在編輯的時候,要判斷當前的狀態是草稿態還是已發布,如果是已發布,是要創建一條新的記錄(當然這個在前端判斷也是可以的,但后端要做好校驗,防止頁面沒刷新等場景造成臟數據)。
消費端
消費端的查詢,需要查詢最新已發布版本,一般是通過狀態來過濾,比如status = Online。
但這里根據狀態過濾有一個問題:歷史已發布的版本怎么辦?如果一個code發布了3個版本,那豈不是會查出來3條數據?要解決這個辦法有兩種思路:
- 狀態機添加一個Online_history的狀態,在寫入的時候維護這種狀態;
- 表增加一個is_last_online_version,在寫入的時候維護這個字段;
我個人比較喜歡用第一種方案,少維護一個字段,僅僅多維護一個枚舉就行了。
其它注意事項
上下游
我們在上下游的接口交互中,除了要根據code?查最新已發布版本這種消費端場景外,通常用code_version來交互。這樣在DB中可以直接命中一條數據,查詢起來也方便。
這個數據和其他數據的關系,也通常使用code_version來存,因為不同版本關聯的數據可能不同。
diff
diff往往是利用領域模型json化后來diff。這里的diff能力可以做成一個通用的服務,傳入old json和new json,返回哪些是新增的,哪些是刪除的,哪些是變更的。內部的邏輯一般是利用json_path和遞歸的方法來做。
diff的難點是做成配置化,配置哪些屬性參與diff,哪些屬性ignore diff。diff出來之后,可能枚舉等需要key轉換成label,外部有一個轉換函數,或者前端去轉。
另一塊需要注意的就是數組的順序。有些字段雖然到json是數組,但業務上本身是順序無關的,這種數據的比對會更麻煩一些。
回滾
回滾其實很麻煩,包括已發布的回滾到上一個版本、發布中的回滾到草稿態。主要是前者很麻煩,尤其是有上下游使用了這個版本的數據,一般是不允許輕易回滾的。
如果有這類場景,多半是沒有上下游,比如服務發布、應用發布等。這種回滾,當前版本的數據一般也不會刪除,而是設置成一個特殊的狀態。下次編輯上一個版本的時候,生成的version也不是+1, 而是+2甚至是+n,還得查一遍庫,比較麻煩。
所以如果不是有特殊的需求,可以不做已發布的回滾,它會帶來很多復雜性。