天天看點

阿裡巴巴編碼規範解讀(六、七)-工程結構及設計規約工程結構設計規約

工程結構

應用分層

1.【推薦】

圖中預設上層依賴于下層,箭頭關系表示可直接依賴,如:開放接口層可以依賴于 Web層,也可以直接依賴于 Service層,依此類推。

阿裡巴巴編碼規範解讀(六、七)-工程結構及設計規約工程結構設計規約

開放接口層:可直接封裝 Service 方法暴露成RPC 接口;通過Web 封裝成http 接口;網關控制層等。

終端顯示層:各個端的模闆渲染并執行顯示的層。目前主要是velocity 渲染,JS 渲染,JSP 渲染,移 動端展示等。

Web 層:主要是對通路控制進行轉發,各類基本參數校驗,或者不複用的業務簡單處理等。

Service 層:相對具體的業務邏輯服務層。

Manager 層:通用業務處理層,它有如下特征:

1) 對第三方平台封裝的層,預處理傳回結果及轉化異常資訊。

2) 對 Service 層通用能力的下沉,如緩存方案、中間件通用處理。

3) 與 DAO層互動,對多個DAO的組合複用。

• DAO層:資料通路層,與底層 MySQL、Oracle、Hbase、OB等進行資料互動。

• 外部接口或第三方平台:包括其它部門 RPC開放接口,基礎平台,其它公司的 HTTP 接口。

個人了解:這個工程結構其實是非常重要的。很多公司對這個結建構的亂七八糟的,像某的這種Globle 500 的項目工程結構也沒有統一的标準。而在京東這種網際網路企業确是有明确規範的。在使用Dubbo與springcloud架構下的工程結構是不一樣的。
開放接口層:如果是采用dubbo做為微服務架構,一般在垂直拆分應用中,一般是采用單獨的一個module提供rpc接口。供内部服務依賴調用。而使用spring cloud,個人建議使用一個包就可以。
//TODO 這個地方以後再補充。
           

2.【參考】

(分層異常處理規約)在 DAO 層,産生的異常類型有很多,無法用細粒度的異常進行 catch,使用 catch(Exception e)方式,并 throw new DAOException(e),不需要列印日志,因為日志在 Manager/Service層一定需要捕獲并列印到日志檔案中去,如果同台伺服器再打日志, 浪費性能和存儲。在 Service層出現異常時,必須記錄出錯日志到磁盤,盡可能帶上參數資訊, 相當于保護案發現場。Manager層與 Service同機部署,日志方式與 DAO層處理一緻,如果是 單獨部署,則采用與 Service一緻的處理方式。Web層絕不應該繼續往上抛異常,因為已經處 于頂層,如果意識到這個異常将導緻頁面無法正常渲染,那麼就應該直接跳轉到友好錯誤頁面, 盡量加上友好的錯誤提示資訊。開放接口層要将異常處理成錯誤碼和錯誤資訊方式傳回。

個人了解:這個地方可以做為強制規範,因為日志的規範列印,對于線上問題的處理能起到很大作用。
這段文字大概是這個意思:
Web層可以通過切面需要統一做異常處理,不允許往外抛異常。
Dao層因為異常種類太多,直接向外抛異常即可。
Service層/Manager層:service層需要列印日志。manager層是否列印日志,與部署關系有關。在大廠中,rpc服務一般是單獨部署的,這個時候service與manger層是分在兩個不同的程序中,此時,manager需要捕獲異常并列印日志,很多開發工程師為了偷懶,在rpc上做切面全部切出日志。如果manager與service是在同一個程序中,則向Dao一樣抛出異常即可。

           

3.【參考】分層領域模型規約

  1. DO(Data Object):此對象與資料庫表結構一一對應,通過 DAO層向上傳輸資料源對象。
  2. DTO(Data Transfer Object):資料傳輸對象,Service 或Manager 向外傳輸的對象。
  3. BO(Business Object):業務對象,可以由Service層輸出的封裝業務邏輯的對象。
  4. Query:資料查詢對象,各層接收上層的查詢請求。注意超過2 個參數的查詢封裝,禁止使用 Map 類 來傳輸。
  5. VO(View Object):顯示層對象,通常是 Web 向模闆渲染引擎層傳輸的對象。
個人建議:其實這個命名是可以。但是很多時候使用BeanCopyUtils類來拷貝對象,在大對象拷貝時,很容易影響性能。是以我個人建議,如果是采用springcloud,如果對象不需要加工,就不需要這麼多層對象轉換。如果是需要轉換,則這麼命名。在dubbo架構下,則可以按照這個規範使用,但是能不對象轉化,就不要對象轉換。尤其是大對象。
           

二方庫依賴

所謂二方庫是指公司内部的依賴jar包。三方庫一般是指其他公司的jar包。

1. 【強制】

定義 GAV遵從以下規則:

1) GroupID 格式:com.{公司/BU }.業務線 [.子業務線],最多4 級。

說明:{公司/BU}

例如:alibaba/taobao/tmall/aliexpress 等BU 一級;子業務線可選。

正例:com.taobao.jstorm 或 com.alibaba.dubbo.register

2) ArtifactID 格式:産品線名-子產品名。語義不重複不遺漏,先到中央倉庫去查證一下。

正例:dubbo-client / fastjson-api / jstorm-tool

3) Version:詳細規定參考下方

2.【強制】

二方庫版本号命名方式:主版本号.次版本号.修訂号

1)主版本号:産品方向改變,或者大規模 API不相容,或者架構不相容更新。

2)次版本号:保持相對相容性,增加主要功能特性,影響範圍極小的 API不相容修改。

3) 修訂号:保持完全相容性,修複BUG、新增次要功能特性等。

說明:注意起始版本号必須為:1.0.0,而不是0.0.1。

反例:倉庫内某二方庫版本号從 1.0.0.0 開始,一直默默“更新”成 1.0.0.64,完全失去版本的語義資訊。

3.【強制】

線上應用不要依賴 SNAPSHOT版本(安全包除外);

正式釋出的類庫必須先去中央倉庫進行查證,使RELEASE版本号有延續性,且版本号不允許覆寫更新。

說明:不依賴SNAPSHOT 版本是保證應用釋出的幂等性。另外,也可以加快編譯時的打包建構。

個人了解:尤其在多個并行分支的情況下,不要在生産依賴于SNAPSHOT版本。某的其實在這塊的管理是沒有太多關注的。
           

4.【強制】

二方庫的新增或更新,保持除功能點之外的其它jar包仲裁結果不變。如果有改變, 必須明确評估和驗證。

說明:在更新時,進行 dependency:resolve 前後資訊比對,如果仲裁結果完全不一緻,那麼通過 dependency:tree 指令,找出差異點,進行排除 jar 包。

5.【強制】

二方庫裡可以定義枚舉類型,參數可以使用枚舉類型,但是接口傳回值不允許使用枚舉類型或者包含枚舉類型的 POJO對象。

就是說不要在Java類中包含枚舉類,而應該單獨定義枚舉類。并且在rpc調用過程中傳回值不應該是枚舉類型。
           

6.【強制】

依賴于一個二方庫群時,必須定義一個統一的版本變量,避免版本号不一緻。 說明:依賴springframework-core,-context,-beans,它們都是同一個版本,可以定義一個變量來儲存版 本:${spring.version},定義依賴的時候,引用該版本。

7.【強制】

禁止在子項目的 pom依賴中出現相同的 GroupId,相同的 ArtifactId,但是不同的 Version。

說明:在本地調試時會使用各子項目指定的版本号,但是合并成一個 war,隻能有一個版本号出現在最後的 lib 目錄中。曾經出現過線下調試是正确的,釋出到線上卻出故障的先例。

8.【推薦】

底層基礎技術架構、核心資料管理平台、或近硬體端系統謹慎引入第三方實作。

9.【推薦】

所有 pom檔案中的依賴聲明放在語句塊中,所有版本仲裁放在 語句塊中。

說明:裡隻是聲明版本,并不實作引入,是以子項目需要顯式的聲明依賴, version 和scope 都讀取自父 pom。而所有聲明在主 pom的裡的依 賴都會自動引入,并預設被所有的子項目繼承。

10.【推薦】

二方庫不要有配置項,最低限度不要再增加配置項。

11.【推薦】

不要使用不穩定的工具包或者Utils類。

說明:不穩定指的是提供方無法做到向下相容,在編譯階段正常,但在運作時産生異常,是以,盡量使用 業界穩定的二方工具包。

12.【參考】

為避免應用二方庫的依賴沖突問題,二方庫釋出者應當遵循以下原則:

1)精簡可控原則。移除一切不必要的 API和依賴,隻包含 Service API、必要的領域模型對象、Utils類、 常量、枚舉等。如果依賴其它二方庫,盡量是 provided 引入,讓二方庫使用者去依賴具體版本号;無log 具體實作,隻依賴日志架構。

2)穩定可追溯原則。每個版本的變化應該被記錄,二方庫由誰維護,源碼在哪裡,都需要能友善查到。除 非使用者主動更新版本,否則公共二方庫的行為不應該發生變化。

伺服器

1. 【推薦】

高并發伺服器建議調小 TCP協定的 time_wait逾時時間。

說明:作業系統預設 240 秒後,才會關閉處于 time_wait 狀态的連接配接,在高并發通路下,伺服器端會因為 處于 time_wait 的連接配接數太多,可能無法建立新的連接配接,是以需要在伺服器上調小此等待值。

正例:在linux伺服器上請通過變更/etc/sysctl.conf 檔案去修改該預設值(秒):      
net.ipv4.tcp_fin_timeout = 3
           

2.【推薦】

調大伺服器所支援的最大檔案句柄數(File Descriptor,簡寫為 fd)。 說明:主流作業系統的設計是将 TCP/UDP 連接配接采用與檔案一樣的方式去管理,即一個連接配接對應于一個 fd。 主流的linux伺服器預設所支援最大fd數量為1024,當并發連接配接數很大時很容易因為fd不足而出現“open too many files”錯誤,導緻新的連接配接無法建立。建議将 linux伺服器所支援的最大句柄數調高數倍(與服 務器的記憶體數量相關)。

3.【推薦】

給JVM環境參數設定-XX:+HeapDumpOnOutOfMemoryError參數,讓JVM碰到OOM 場景時輸出dump資訊。

說明:OOM 的發生是有機率的,甚至相隔數月才出現一例,出錯時的堆内資訊對解決問題非常有幫助。

個人了解:這點非常重要。之前一個線上OOM問題,并沒有列印堆棧資訊,對于定位問題很不利。
           

4.【推薦】

線上上生産環境,JVM的 Xms和 Xmx設定一樣大小的記憶體容量,避免在 GC 後調整 堆大小帶來的壓力。

5.【參考】

伺服器内部重定向必須使用 forward;外部重定向位址必須使用 URL Broker生成,否 則因線上采用HTTPS協定而導緻浏覽器提示“不安全“。此外,還會帶來 URL維護不一緻的 問題。

設計規約

1.【強制】

存儲方案和底層資料結構的設計獲得評審一緻通過,并沉澱成為文檔。

說明:有缺陷的底層資料結構容易導緻系統風險上升,可擴充性下降,重構成本也會因曆史資料遷移和系 統平滑過渡而陡然增加,是以,存儲方案和資料結構需要認真地進行設計和評審,生産環境送出執行後, 需要進行 double check。

正例:評審内容包括存儲媒體選型、表結構設計能否滿足技術方案、存取性能和存儲空間能否滿足業務發 展、表或字段之間的辯證關系、字段名稱、字段類型、索引等;資料結構變更(如在原有表中新增字段) 也需要進行評審通過後上線。
           

2.【強制】

在需求分析階段,如果與系統互動的User超過一類并且相關的User Case超過5個, 使用用例圖來表達更加清晰的結構化需求。

3.【強制】

如果某個業務對象的狀态超過3個,使用狀态圖來表達并且明确狀态變化的各個觸發 條件。

說明:狀态圖的核心是對象狀态,首先明确對象有多少種狀态,然後明确兩兩狀态之間是否存在直接轉換 關系,再明确觸發狀态轉換的條件是什麼。

正例:淘寶訂單狀态有已下單、待付款、已付款、待發貨、已發貨、已收貨等。比如已下單與已收貨這兩 種狀态之間是不可能有直接轉換關系的。

4.【強制】

如果系統中某個功能的調用鍊路上的涉及對象超過3個,使用時序圖來表達并且明确各調用環節的輸入與輸出。

說明:時序圖反映了一系列對象間的互動與協作關系,清晰立體地反映系統的調用縱深鍊路。

5.【強制】

如果系統中模型類超過5個,并且存在複雜的依賴關系,使用類圖來表達并且明确類之間的關系。 說明:類圖像建築領域的施工圖,如果搭平房,可能不需要,但如果建造螞蟻 Z空間大樓,肯定需要詳細 的施工圖。

個人了解:這塊其實我自己很多時候都沒有做到。很多時候隻做了概要設計就開始開發了。對重要或核心的流程才用了思維導圖做了。這塊很多公司應該都沒有做類圖的設計吧。對于類圖的編寫,可以參考rocketMQ的文檔。
           

6.【強制】

如果系統中超過2個對象之間存在協作關系,并且需要表示複雜的處理流程,使用活 動圖來表示。 說明:活動圖是流程圖的擴充,增加了能夠展現協作關系的對象泳道,支援表示并發等。

7.【推薦】

系統架構設計時明确以下目标:

⚫ 确定系統邊界。确定系統在技術層面上的做與不做。

⚫ 确定系統内子產品之間的關系。确定子產品之間的依賴關系及子產品的宏觀輸入與輸出。

⚫ 确定指導後續設計與演化的原則。使後續的子系統或子產品設計在一個既定的架構内和技術方向上繼續演化。

⚫ 确定非功能性需求。非功能性需求是指安全性、可用性、可擴充性等

很多時候後續的設計與演化是與需求相關的,技術人員或架構師必須知道以後産品的疊代方向,是以,要求需求的提供者能盡早對産品的發展趨勢做規範。
           

8.【推薦】

需求分析與系統設計在考慮主幹功能的同時,需要充分評估異常流程與業務邊界。 反例:使用者在淘寶付款過程中,銀行扣款成功,發送給使用者扣款成功短信,但是支付寶入款時由于斷網演 練産生異常,淘寶訂單頁面依然顯示未付款,導緻使用者投訴。

個人了解:設計時需要對重點主幹流程做分析,這塊是不能出現偏差的。對這塊的設計過程中,需要重點考慮異常流程及業務邊界。
異常流程包括:分布式事務退回方案。交易的幂等與狀态,流程的合理及黑客攻擊性。比如圖形驗證碼的業務流程。該業務流程的邊界和發展趨勢。
           

9.【推薦】

類在設計與實作時要符合單一原則。

說明:單一原則最易了解卻是最難實作的一條規則,随着系統演進,很多時候,忘記了類設計的初衷。

類設計的五個原則:
單一職責原則:要求類隻負責一件事情。
它規定一個類應該隻有一個發生變化的原因。将不同的職責封裝到不同的類或子產品中。

開閉原則:要求類不作修改而能夠擴充功能,展現了類的封裝與繼承。
開閉原則指的是開放封閉原則,即對擴充開放,對修改封閉。
所謂修改封閉,就是之前設計好的類,不要去修改。比如删除掉一個成員函數、改變成員函數的形參清單或更改資料成員類型等。實作對修改封閉,關鍵在于抽象化。對一個事物抽象化,實質上是對一個事物進行概括、歸納、總結,将其本質特征抽象地用一個類來表示,這樣類才會相對穩定,無需更改。
所謂擴充開放,就是在不改變已存在的類的前提下可以添加很多功能。一般是通過繼承和多态來實作,如此一來,可以保持父類的原樣,隻需在子類中添加些所需的新功能。

接口隔離原則:讓客戶隻關心他們所需的接口。單一職責原則與接口分離原都展現了内聚的思想;
使用多個小的專門的接口,而不要使用一個大的總接口。具體而言,接口應該是内聚的,應該避免“胖”接口。一個類對另一個類的依賴應該建立在最小的接口上,而不要強迫依賴不同的方法,這是一種接口污染。
在某司系統中,某個服務的所有方法都是通過該接口的invoke方法來實作的。這麼個胖子,怎麼一個了得。


裡氏替換原則:要求派生類要能夠替換基類,是對類繼承的規範。
子類可以替換父類并出現在父類能夠出現的任何地方。

依賴倒置原則:
其核心思想是:依賴于抽象。具體而言就是高層子產品不依賴于底層子產品,二者都依賴于抽象;抽象不依賴于具體,具體依賴于抽象。依賴倒置原則是對傳統過程性設計方法的“倒轉”,是高層次子產品複用及其可維護性的有效規範。
這個就是面向對象開發中的抽象。
           

10.【推薦】

謹慎使用繼承的方式來進行擴充,優先使用聚合/組合的方式來實作。

說明:不得已使用繼承的話,必須符合裡氏代換原則,此原則說父類能夠出現的地方子類一定能夠出現, 比如,“把錢交出來”,錢的子類美元、歐元、人民币等都可以出現。

11.【推薦】

系統設計階段,根據依賴倒置原則,盡量依賴抽象類與接口,有利于擴充與維護。 說明:低層次子產品依賴于高層次子產品的抽象,友善系統間的解耦。

因為類的設計是詳細設計的範疇,是以在詳細設計中,可以定義抽象類和接口。
           

12.【推薦】

系統設計階段,注意對擴充開放,對修改閉合。

說明:極端情況下,傳遞的代碼是不可修改的,同一業務域内的需求變化,通過子產品或類的擴充來實作。

應該盡量向這種極端情況看齊。
           

13.【推薦】

系統設計階段,共性業務或公共行為抽取出來公共子產品、公共配置、公共類、公共方法等,在系統中不出現重複代碼的情況。

說明:随着代碼的重複次數不斷增加,維護成本指數級上升。

14.【推薦】

避免如下誤解:靈活開發 = 講故事 + 編碼 + 釋出。

說明:靈活開發是快速傳遞疊代可用的系統,省略多餘的設計方案,摒棄傳統的審批流程,但核心關鍵點上 的必要設計和文檔沉澱是需要的。

反例:某團隊為了業務快速發展,靈活成了産品經理催進度的借口,系統中均是勉強能運作但像面條一樣的代碼,可維護性和可擴充性極差,一年之後,不得不進行大規模重構,得不償失。

一個系統應該要考慮滿足未來三年的業務,不然頻繁進行重構,導緻系統穩定性極差。
           

15.【參考】

設計文檔的作用是明确需求、理順邏輯、後期維護,次要目的用于指導編碼。

說明:避免為了設計而設計,系統設計文檔有助于後期的系統維護和重構,是以設計結果需要進行分類歸檔儲存。

個人建議,概要設計是必須的,如果隻有概要設計,那建議由架構師編寫主要的接口及評估核心流程的技術方案。如果有詳細設計,則需要補充類圖等。不要為了設計而設計。設計的重點是明确需求,理順邏輯,後期維護。
           

16.【參考】

可擴充性的本質是找到系統的變化點,并隔離變化點。

說明:世間衆多設計模式其實就是一種設計模式即隔離變化點的模式。

正例:極緻擴充性的标志,就是需求的新增,不會在原有代碼傳遞物上進行任何形式的修改。

17.【參考】

設計的本質就是識别和表達系統難點。

說明:識别和表達完全是兩回事,很多人錯誤地認為識别到系統難點在哪裡,表達隻是自然而然的事情, 但是大家在設計評審中經常出現語焉不詳,甚至是詞不達意的情況。準确地表達系統難點需要具備如下能力: 表達規則和表達工具的熟練性。抽象思維和總結能力的局限性。基礎知識體系的完備性。深入淺出的 生動表達力。

18.【參考】

代碼即文檔的觀點是錯誤的,清晰的代碼隻是文檔的某個片斷,而不是全部。

說明:代碼的深度調用,子產品層面上的依賴關系網,業務場景邏輯,非功能性需求等問題是需要相應的文 檔來完整地呈現的。

尤其是複雜業務的深層調用,可以考慮用類圖來呈現。
           

19.【參考】

在做無障礙産品設計時,需要考慮到:

⚫ 所有可互動的控件元素必須能被 tab 鍵聚焦,并且焦點順序需符合自然操作邏輯。

⚫ 用于登陸校驗和請求攔截的驗證碼均需提供圖形驗證以外的其它方式。

⚫ 自定義的控件類型需明确互動方式。

正例:使用者登陸場景中,輸入框的按鈕都需要考慮 tab鍵聚焦,符合自然邏輯的操作順序如下,“輸入用 戶名,輸入密碼,輸入驗證碼,點選登入”,其中驗證碼實作語音驗證方式。如果有自定義标簽實作的控 件設定控件類型可使用 role 屬性。