后端Coder如何做好代码设计?
點擊上方“朱小廝的博客”,選擇“設為星標”
后臺回復"書",獲取
來源:r6d.cn/C5Ja
說明:生鮮電商屬于一個軟件的產品,那么如何做好代碼設計呢?代碼設計,是程序員做項目時,在coding之前非常重要的一個步驟,可以說關系到整個系統、整個團隊的研發質量和效率。一般說代碼設計,可能涵蓋以下幾種:
整體設計
架構設計
領域模型設計
數據庫設計
API設計
代碼實現設計
代碼設計的前提是,項目組成員已經完成正式的需求評審,并經過充分思考:
這個需求是為什么業務目標服務的?
這個需求描述的內容,是否為服務該目標最合適的方式(包括研發性價比、項目周期等)?
prd本身是否邏輯自洽?
是否每個內容點都可實現?
實現的技術方案是什么?
是否做過類似的功能,合并嗎?復用嗎?拆解嗎?
整體設計與架構設計
“項目的整體設計,有時會涵蓋系統架構設計,這里要區分一下,系統架構設計并不完全等同于代碼架構設計。
”整體設計首先要考慮的,是當前項目是要做一個全新的項目,還是要做原有項目基礎上改造、迭代;項目組的積累中,是否有可以復用的地方(模塊或成熟方案),是否有可以通過改造以符合新項目需求的可能。
其次再考慮,如果是新起項目,要如何搭建整體實施方案,內容一般包括:
硬件部署與資源申請:硬件和資源,是要和業務需求結合來制定的,比如業務最大訪問、TPS/QPS等,要切實討論得出一個數據范圍,以確定系統是否做高并發方案。另外還要考慮容災等問題,以此制定系統高可用的設計。
分析項目特別突出的業務、技術難點:如千人千面的UI和查詢,或靈活配置的業務模式,類似這種需求的項目,會在模塊模型設計上做額外處理,可能是將各種規則單獨做一層規則引擎,也可能是在數據建模時增加更多維度;再比如超大的QPS,可能要在整體架構上,添加額外的中間層,異步收集數據等功能;還有就是依賴于審核的迭代上線周期(IOS、微信小程序)等,都要在整體設計中考慮進去。
內外部信息交換通訊:整體設計系統,要明確劃定項目范圍,哪些是本系統的功能,哪些功能、數據依賴其它系統或模塊。要明確此次項目對外交互系統的訪問關系和訪問條件。
數據的持久化存儲方案,如何選擇硬件,如何選擇軟件,一般有常規解決方案。特別要考慮和需求結合的一點是業務數據的生命周期,這也是數據歸檔方案等重要依據。
整體設計中非常重要的一部分是系統架構設計,要在業務邊界確定的前提下進行。
”首先,從業務視角進行拆解功能,定義系統由哪些模塊構成。
其次,根據業務復雜度、模塊功能性劃分,選擇合理的軟件架構模式及方案。
比如,業務需求上,有非常復雜的流程處理,但各個業務子模塊之間相對獨立,則可以選擇事件驅動型架構,基本上用狀態機、工作流就可以滿足功能了;再比如做微服務架構設計上,也不一定都做集中式負載均衡,如果整體功能需求對消息流轉要求比較高,則可以考慮中心消息型服務(比如Rocketmq)。
以上兩步更多是從需求上分析,偏近于從業務角度設計系統。后面兩步要從技術上做架構設計。
下一步要根據模塊劃分和架構方案選擇合適的開發語言和技術框架。生產上一般考慮優選Java語言+Spring框架,但也有許多混搭的成熟方案,有些基于JVM的開源框架中對多語言的支持還是比較好的。
再下一步要將系統拆解,一般考慮劃分出展現層、(通訊層)服務層、數據層,再根據需求為每一層設計合理的服務及組件。如展現層上web還是mobile,服務層是用Spring Boot還是Spring Cloud做微服務,數據層使用Mysql還是Oracle等。另外還有項目服務等通用組件及各種業務、技術中間件、及整體項目通用的技術方案選型,如日志(ELK等)、監控告警,訪問授權,token認證等。
整體設計要在需求明確(不一定所有細節完全敲定)的前提下盡心,一般形成定案要在正式需求評審會之后。
整體設計由團隊leader或項目owner完成。
整體設計文檔,優先考慮使用物理部署圖、邏輯架構圖、業務流程圖等描述系統架構。
另:特別建議在需求評審會后,首先由研發lead組織召開設計討論會議。目的是讓項目成員盡早的參與到項目之中,并使用討論到方式,更好的理解項目及整體設計方案。
一般設計討論可用頭腦風暴方式進行會議。有重大分歧時,可以投票表決。
此討論會議召開前,需要leader提前設定議題,會議上設有1名主持人(可leader兼任或者team member輪流擔任),按照議題集中,輪流發言再集體討論方式得出結論。
主要議題包括:整體設計方向、項目人員大致分工、待解決疑問和處理人。
領域模型設計與數據庫設計
在整體架構設計完成后, 要針對已經拆分的系統模塊做模型設計, 尤其是在項目需求中有重要功能的部分要重點設計。領域建模要深度結合需求,從業務角度出發,用一種自頂而下的方式來建立模型。
領域建模方法很多,最重要的原則是在項目模型建立之前,要先做概念的統一整理,要對特定概念的名詞做專業命名及能力約束。在此基礎上,再進行重疊功能的歸并和抽象。需要注意的是,此處制定的模型,和業務需求、數據庫設計、代碼類設計,都是一脈相承的,但并不完全等同。比如需求中有訂單Order的概念,在設計訂單Order都有哪些元素構成,可以實現什么操作時會發現,要在數據庫和代碼中,拆分為OrderHeader和OrderDetail。
在領域模型中,還有一個重點,是要標注清楚各抽象概念之間的數據關系和約束。一般會比較關注數據之間是一對一、一對多、多對多等關系,并在此基礎上,結合業務流程泳道做系統模塊依賴關系圖、數據流圖等。
數據庫一般結合領域模型設計,是領域模型持久化存儲的映射轉化(ORMapping)。在項目數據庫設計中,除了常規關注的范式、mysql約束等(單獨寫過mysql應用的usage,此文略),額外關注:
1、表字段在整體系統中的規范定義和統一使用。比如相同的概念,在各個表字段中的定義要一致,(在代碼應用中也應保持一致)。
2、數據庫事務的應用方案。
3、冗余字段與代碼簡潔實用的平衡。
4、特別說明,數據庫一般僅作數據的持久化存儲,不要將業務邏輯的處理放到數據庫中進行。
5、生產業務數據delete,應該僅作邏輯刪除,不做物理刪除。
6、表設計要和性能結合起來,特別當DB成為性能瓶頸時,要特別斟酌索引設計的合理性。
模型設計一般為團隊leader或項目owner完成,數據庫設計一般為leader帶領team member一起完成。
數據庫設計一般要先于具體功能代碼實現,在做此設計后,要針對存儲方案和底層數據結構設計,做double check和集中評審,評審內容包括存儲介質選型、表結構設計能否滿足技術方案、存取性能和存儲空間能否滿足業務發展、表或字段之間的辯證關系、字段名稱、字段類型、索引等。在評審一致通過后,沉淀為文檔。
API設計與代碼實現設計
在完成整體設計后, 要將所有功能拆分成獨立可調用的API。 這里的設計要考慮系統實現和業務需求的結合。 系統API拆分,一定是從需求實現角度考慮, 基于領域能力做的,且要充分考慮后續需求的變化演進, 盡量使更多后續功能變化在既有規定服務API的框架內繼續演化。 此外還要關注非功能性需求,比如安全性、可用性、可擴展性等。 最后,在這個步驟要關注的點, 除了系統主干正向流程外,還有逆向流程、異常流程、 業務邊界等方面的接口定義。API是系統模塊對外提供的服務,現行系統基本滿足前后端分離的框架使用條件,所以一般可以簡單理解為前端和系統交互的入口。但實際系統設計中,API不僅僅提供給前端使用,所以每個API的實現設計是系統模塊設計中非常重要的環節。
API的調用方一般會考慮給展示層多端調用(web、mobile等),還要考慮相同的API是否可以給其它系統模塊使用,最后一層設計是,是否用相同的API對外提供openAPI服務。設計中應當特別考慮此類API的功能聚合、分離,多場景適用性等。以訂單列表查詢為例,設計一個訂單列表query接口:
給前端頁面查詢,前端分頁每次傳參。這個場景最大的特點是,查詢頁面會設計較多分類查詢的篩選條件,且此類設計實現經常可能是查詢條件直接投射到DB上。
在整體系統中,給其它模塊使用,如用戶模塊或報表模塊。這個場景的特點是,如果其它模塊功能為同步調用,則QPS不可預測,比如用戶模塊使用了這個訂單query接口,那么這個接口的性能就會成為用戶模塊的瓶頸。還有一種可能是其它模塊用此接口異步訪問同步數據,就很有可能采用定時任務方式,固定分頁,并發調用查詢,如每5分鐘調用一次,每次調用并發20,每個訪問查詢500分頁數據。
給openAPI使用。如果開放給前端的query服務提供給開放平臺直接使用或包裝后直接訪問,則容易出現的場景是,每次調用查詢不確定分頁,很有可能一個大分頁(如十萬)就打到DB上,這樣即使索引匹配也容易造成數據庫緩存區擁堵。遇到這類需求,
3.1 要考慮一個API接口是否可以滿足所有需求,是否對數據訪問做權限隔離。即,考慮所有的服務都集中到一個API上,還是定向拆分,將一個內部實現core,分別投射到多個API上。
3.2 不同訪問端如果有不同的QPS需求,還都考慮到,單個特大QPS接口,可以橫向合并,即,不根據業務約束,而是把所有大訪問的接口拆出來,給到單獨技術架構和硬件部署的服務里。
3.3 是否內部實現上一致,是否使用緩存、中間層方案等。
數據庫設計尤其是索引設計要和接口設計(尤其是篩選條件)保持一致。
API的設計還有一個維度的考慮,是基于數據交互考慮。當兩個系統模塊要使用API交互數據時,定義的API要充分考慮使用場景,并做技術選型:
4.1. 數據是推還是拉?
4.2. 同步推送還是異步通知回調?
4.3. 通過接口還是MQ?是否需要削峰?
4.4. 是否需要保證強一致性?
4.5. crontab定時發起還是任務隊列發起?需要延時隊列、死信隊列嗎?
API拆分完成后,要做代碼實現設計。此設計主要關注每個API的內部實現,將一系列領域模型轉換為系統對象的類設計。這里面有3個圖可以輔助設計:
1、如果一個API復雜度較高,調用鏈路上的涉及對象較多,可以使用時序圖來表達并且明確各調用環節的輸入與輸出,以反映對象間的交互與協作關系。時序圖對完成設計評審、輔助項目開發有很大作用。
2、如果某個業務對象的狀態較多,可以使用狀態圖來表達并且明確狀態變化的各個觸發條件。首先明確對象有多少種狀態,然后明確兩兩狀態之間是否存在直接轉換關系,再明確觸發狀態轉換的條件是什么。狀態圖對測試用例有很大幫助。
3、如果系統中模型類超過較多,且存在復雜的依賴關系,可以使用類圖來表達并且明確類之間的關系。類圖對復雜系統設計,尤其是靈活配置、路由映射、設計模式應用等,有一定幫助。
類的設計要充分考慮單一原則。應當優先使用聚合/組合的方式來實現。不得已使用繼承的話,要使父類能夠出現的地方子類一定能夠出現。根據依賴倒置原則,盡量依賴抽象類與接口,有利于系統的擴展與維護。
在設計抽象時,要考慮以下問題:代碼直觀嗎(好的代碼自注釋性很強),它的編寫巧妙嗎?實現細節可能隱去了嗎?程序編寫是立足于問題域而不是計算機科學或語言結構域嗎?
程序開發有一個場景比較典型,寫第一版需求時,僅僅是一個簡單功能,實現也比較簡單,但后續功能增加很多,變化很大,每次在原有類定義基礎上增加功能,倒置代碼冗余,尤其容易造成if-else太多。此時要考慮提前預估功能,做擴展性設計,或者在每次功能迭代中,做小版本重構。比如訂單明細查詢,在定義查詢接口(interface)后,需求要增加一個千人千面功能,不同用戶訪問返回的內容條目不一樣。如果用if-else或switch寫,會比較不好管理,代碼也容易混亂,這里可以新設計一個接口,做不同內容配置,然后組合使用,或者采用其它設計模式。
設計模式的目的,是輔助程序員更好的實現代碼抽象,將現實業務邏輯,映射到抽象維度的代碼語言上。一般生產上經常用到工廠抽象工廠、模板方法、策略、狀態等。選擇合適的設計模式和數據結構,有助于提升代碼的清晰簡潔度。
這個層次的代碼設計一般交給team member完成,并輸出接口定義、接口詳細設計、包括一些數據庫的DDL等。
設計評審與文檔中心
在項目實施之前,設計評審是非常重要的環節。研發lead應該組織內部設計討論,每人的接口設計要通過peer或backup的review,更好的方式是通過集中評審。
因為再好的設計,也要確保項目組所有成員,理解正確且一致。這個不僅僅是lead對團隊的灌輸,要確保組員之間對同一個業務概念的理解也是正確且一致的。要確保每人的接口設計,都符合整體設計需要。要確保項目級別領域定義符合更上層定義(如公司級別命名),要確保項目級別領域定義統一、代碼實現中英文命名統一。大型項目,在dev內部設計通過后,也可以由項目經理組織產品、研發、測試各方展開設計評審,此處尤其要和prd結合,為整理測試用例服務。
接口細節的實現也應當是動手coding之前先做設計,并建議形成文檔,研發lead要提前組織好開發文檔中心,整組統一設計文檔格式。
一般常規內容包括:
?新項目背景、或常規迭代項目里程碑
?項目管理的時間節點(需求評審時間、設計時間、提測時間、上線時間點)
?本期項目概要設計說明
?分工(API、完成人、預估工時、實際工時等)
?詳細設計:接口實現設計、DB設計、緩存設計等
?上線計劃等等
想知道更多?掃描下面的二維碼關注我
后臺回復"技術",加入技術群
【精彩推薦】
原創|OpenAPI標準規范
如此簡單| ES最全詳細使用教程
ClickHouse到底是什么?為什么如此牛逼!
原來ElasticSearch還可以這么理解
面試官:InnoDB中一棵B+樹可以存放多少行數據?
微服務下如何解耦?對于已經緊耦合下如何重構?
如何構建一套高性能、高可用、低成本的視頻處理系統?
架構之道:分離業務邏輯和技術細節
星巴克不使用兩階段提交
點個贊+在看,少個 bug?????
總結
以上是生活随笔為你收集整理的后端Coder如何做好代码设计?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CTO要我把这份MySQL规范贴在工位上
- 下一篇: 收藏:DPDK内存基本概念