最近我一直在思考幾個問題:
前端
業務代碼究竟難不難寫?程序員
一直開發業務代碼是否是徹底學不到東西?面試
5年+開發經驗的老程序員的價值在哪裏?sql
如何經過面試來區分業務代碼開發的水平?數據庫
其實,這幾個問題或多或少是相互關聯的。有的時候你們也會自嘲說,「程序員接手的代碼永遠是爛攤子,而後本身繼續在這個爛攤子上產出代碼,留給又一波後人接手」。十幾年來經歷過十來個公司,我看了很多差的代碼,也看了很多好的代碼,本身產出過垃圾代碼,也帶領團隊實現過一些自認爲不錯的代碼。後端
你可能會說,業務代碼就是增刪改查,和框架代碼的難度不能比,徹底是機械勞動,其實我以爲不徹底是這樣,甚至徹底不是這樣,我我的認爲寫出能跑的業務代碼不難,但要寫出好的業務代碼實際上是挺難的,更重要的是若是系統設計的足夠好,在很長一段時間內系統的可維護性是可控的,只須要簡單擴展便可,若是基礎打的不夠好,那麼項目可能就是一次性項目,下面我列出業務系統我關注的一些點,你想一想是否是有道理。設計模式
若是你對技術提高很感興趣,能夠加入Java高級技術來交流學習:856443934,裏面都是同行,有資源分享和技術進階思惟導圖,其中:(分佈式架構、高可擴展、高性能、高並 發、Jvm性能調優、Spring,MyBatis,Nginx源碼分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。歡迎一到五年的工程師加入,合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!
緩存
標準化安全
標準的項目結構bash
我本身很是注重搭建項目結構的起步過程,模塊的劃分、目錄(包)的命名,我以爲很是重要,若是作的足夠好,別人導入項目後可能只須要10分鐘就能夠大概瞭解結構了。
一、有些名詞是約定俗成的,你們一眼就能看出是啥東西的,好比:controllers、services、configs、utils、commons,還有jobs。比較重要的是肯定先進行分類再分業務,仍是先分業務再分類,在代碼裏混用這兩種風格的結構就會很混亂:
controllers
order
user
jobs
order
user
order
services
mappers
user
services
mappers
對於直筒的三層架構的純數據表驅動的代碼,我建議第一層是分類,第二層是業務功能:看一眼controllers目錄咱們知道項目對外的Api能力,再看一眼services目錄咱們知道項目的邏輯複雜度,最後再看一眼mappers目錄咱們知道項目的表結構。
對於有一些項目,不必定每個邏輯都涉及到三層架構,數據流量比較複雜,我建議是按照業務功能先來分,下一層視狀況也不必定徹底是須要按照組件類型分二級目錄,也能夠是按功能來分:
core
storage
service
dispatcher
engine
context
callback
gateway
handler
notification
sms
push
對於這種目錄結構一眼望去就能知道大概項目數據流和架構了,core對內,dispatcher作分發的感受,callback是外部來的回調數據,notification是通知外部的數據流。這種數據流向複雜的項目,使用這種結構會比前一種合理的多,由於咱們須要先關注數據流,而不是三層結構的層次,甚至對於core、dispatcher、notification咱們知道實際上是沒有controller的。
二、有些名詞可能就須要內部有一個統一,好比不一樣的層次面向數據庫,面向業務,面向UI,面向RPC須要有不一樣的POJO,咱們須要明確一套對應的命名,能明確就好,好比下面的這些POJO咱們其實挺難分辨其用途的,須要進行規範,而且放置於匹配的目錄結構中:
CreateOrderRequest / CreateOrderResponse
CreateOrderParam / CreateOrderDto
咱們能夠約定第一組用於服務自己訪問外部(的Rpc服務也好,REST服務也好),第二組用於服務自己對外提供的Web Api,好比:
controllers
queryOrder()
createOrder()
OrderController
QueryOrderParam
QueryOrderDto
CreateOrderParam
CreateOrderDto
rpcs
login()
register()
UserService
LoginRequest
LoginResponse
RegisterRequest
RegisterResponse
services
OrderService
OrderServiceImpl
OrderEntity
storages
OrderMapper
OrderModel
總之,雖然可能10+人在維護相同的項目,目錄結構的風格、命名、專用名詞的使用必定要統一。
這個須要在開展項目以前明確下來,我見過有項目中同時使用了Spring MVC和Jersey作Web API,同時使用了Spring Scheduler和Quartz作任務調度。最好是項目開展前明確框架的版本而且搭建好項目腳手架,大概涉及:
Web API / Web MVC
Job Scheduler
Micro Service
Config Center
Redis Client
Data Access
Entity Mapper
MQ Client
固然,咱們也能夠獨立出依賴管理的項目,專門由獨立模塊進行依賴版本管理。最差也要在Wiki上進行明確。
若是項目涉及到對外提供API,那麼很是有必要在初期就規範API的框架定義,涉及到包裝類Result<T>
的定義(見過一個項目用了三種包裝類的)以及遇到錯誤的狀況下,HTTP狀態碼的體現好比這樣的包裝類格式:
public class ApiResult<T> {boolean success; String code; String message; String path; long time; T data;}複製代碼
咱們能夠這麼和客戶端的開發來明確:
即便遇到錯誤,HTTP狀態碼仍是200,HTTP狀態碼若是是500或是404的話那必定是網關層面的錯誤了,這個錯誤不是後端服務返回的;
在HTTP狀態碼仍是200的時候表明收到了後端的返回,前端去按照ApiResult以Json格式反序列化HTTP Body的報文;
而後查看success(若是沒success也行,咱們能夠約定code是200就是成功),若是是success表明後端服務成功處理了請求,若是不成功,則根據後端給的錯誤代碼映射表根據code進行處理或直接提示message中的內容。注意,這裏的success只表明後端是否成功處理了請求,不表明請求表明的業務邏輯是否成功處理。舉一個例子,若是這個請求是異步支付請求,那麼success==true表明前端給的參數都正確,後端正確接受了支付申請,不表明支付成功;
在success==true的狀況下再去解析data中的內容,拿取客戶端須要的信息,仍是前面的例子,data裏能夠是{"orderStatus":"PROCESSING", "orderId":"1234"}
,這個纔是真正業務邏輯的數據和狀態,success並不表明訂單支付操做就是成功的,也多是處理中的狀態
因此這是幾個層次的事情,Http Status->ApiResult.status->ApiResult.data.orderStatus
加解密規範和簽名規範Api的加密解密以及簽名最好在設計的時候就考慮進去,並且要仔細斟酌,不然之後很難變動,特別Api的使用方是客戶端的狀況,客戶端很難輕易強更。若是作SAAS服務,建議參考大廠的規範,好比亞馬遜AWS的API規範或阿里雲API的規範,不建議本身造輪子,大廠作的API規範都是通過安全方面的專家深度思考的。
版本管理規範(好比Url path路由仍是Http header路由)若是使用了老版本的話,是否須要在返回內容中提示新版本的Url、版本號、老版本最後維護時間呢?這個就不展開了
因此統一Api這個事情不只僅是Api的格式還涉及到安全處理、版本處理、客戶端操做方式等等。對於一些須要服務端驅動客戶端的業務(UI邏輯動態)來講,咱們能夠定義一套更復雜的ApiResult,讓服務端告知客戶端這個時候應該是提示仍是跳轉仍是返回等等。
若是你對技術提高很感興趣,能夠加入Java高級技術來交流學習:856443934,裏面都是同行,有資源分享和技術進階思惟導圖,其中:(分佈式架構、高可擴展、高性能、高並 發、Jvm性能調優、Spring,MyBatis,Nginx源碼分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。歡迎一到五年的工程師加入,合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!
如今你們都使用Git,分支如何管理每個公司(在Gitflow的基礎上)都會略有不一樣,也須要和你們明確:
分支的定義(master、develop、release、hotfix、feature);
分支命名規範;
checkout、merge request流程;
提測流程;
上線流程;
Hotfix流程;
別小這個,雖然這個和代碼質量和架構無關,可是梳理清楚能夠:
提升開發和測試的工做效率,人多也亂;
減小甚至杜絕代碼管理致使的線上事故;
讓項目管理者和架構師能夠明確什麼代碼如今在哪裏;
方便運維處理髮布和回滾;
讓項目的開發能夠靈活適應多變的需求;
見過一些項目在實現業務代碼的時候是不考慮任何異常處理、事務處理、鎖處理的,在流量小無併發的狀況下,這些項目不容易爆發出嚴重的問題,基本能用。可是對於高併發的項目或未來可能會高速發展的項目,若是不考慮這些問題會死的很難看。
咱們來想一下,若是如今在設計一個訂單服務,若是由於網絡問題、併發問題致使數據錯亂、服務中斷的可能在千分之一,若是一個業務一天只有1000次請求,1天才遇到這樣1次問題,即便遇到了問題用戶也不必定會來反饋,即便來反饋每每客服也能經過後臺取消訂單等操做來處理,這個問題不會爆發出來,若是一天的單量是1000萬,那麼天天可能就會有10000單異常的訂單,這個可能就超過了客服的處理能力了。
不多有項目真正100%徹底作好了全部細節,只不過每每是由於量小得不到你們的重視罷了。但咱們想一下,若是遇到數據庫或中間件級別大規模故障的狀況下,若是一致性處理的很差,那麼數據庫恢復後可能會留下一大堆異常數據須要修復,若是處理的好,業務數據不會錯亂,數據庫恢復後業務立刻能夠恢復。在遇到事故的時候,系統這方面的設計功力就體現出來了。
在實現代碼的時候須要考慮以下事情:
本地事務處理:見過一些代碼徹底不考慮事務,或者是隻是知其然使用@Transactional
,可是方法內部徹底catch了全部異常的狀況。
事務包含的方法塊;
嵌套事務、事務傳播;
何時遇到什麼異常應該回滾;
@Transactional
是否真正生效了?
外部服務調用的事務問題:
調用外部服務出現異常的時候,本地事務怎麼處理;
調用的外部服務是否容許重試(冪等調用);
調用外部服務出現未知結果後,怎麼進行補償;
補償是否有上限,是否存在死信數據卡死補償的狀況?
若是有2+外部服務連同本地數據庫存儲都須要有事務性,怎麼實現?
數據重複和順序問題:
先本地事務提交仍是先調用外部接口(若是先調用外部接口,可能會遇到外部回調的時候本地事務還沒提交找不到數據的狀況);
從MQ收到的消息順序問題怎麼解決?
從新入MQ的延遲消息或重試消息亂序是否會有問題?
對外提供的Api或回調方法是否支持冪等?
鎖的問題
哪一個層面作鎖?服務層分佈式鎖仍是數據庫層面鎖?
樂觀鎖仍是悲觀鎖?
你確信你的Redis鎖方案是可靠的嗎?
你是否知道多少請求再排隊等待,又是爲何?
這些要作好真的很難,每一步都須要認證考慮,可是很遺憾見過的不少具備複雜業務的代碼,在Service中一連串調用了N個外部服務進行寫操做,方法內也實現了N個表的寫操做,即不考慮外部服務的事務和補償問題,本地也沒有事務控制,出了錯只是打印了堆棧而後客戶端看到的是一個服務器忙。
異常處理不只僅是狹義上遇到了Exception怎麼去處理,還有各類業務邏輯遇到錯誤的時候咱們怎麼去處理。就拿記日誌這一件事情來講:
WARN和ERROR的選擇須要好好考慮,WARN通常我傾向於記錄可自恢復但值得關注的錯誤,ERROR表明了不能本身恢復的錯誤。對於業務處理遇到問題用ERROR不合理,對於catch到了異常也不是全用ERROR。
記錄哪些信息,最好打印必定的上下文(用戶Id、訂單Id、外部傳來的關鍵數據)而不只僅是打印線程棧。
記錄了上下問信息,是否要考慮日誌脫敏問題?能夠在框架層面實現,好比自定義實現logback的ClassicConverter
咱們知道catch到了異常或遇到了業務錯誤,咱們除了記錄日誌還有不少選擇,也須要認真考慮何時應該作什麼:
直接返回
拋出異常
重試處理
恢復處理
熔斷處理
降級處理
甚相當閉業務
這又涉及到了彈力設計的話題,咱們的系統每每會對接各類外部服務、Api,大部分服務都不會有SLA,即便有在大併發下咱們也須要考慮外部服務不可用對本身的影響,會不會把本身拖死。咱們老是但願:
儘量以小的代價經過嘗試讓業務能夠完成
若是外部服務基本不可用,而咱們又同步調用外部服務的話,咱們須要進行自我保護直接熔斷,不然在持續的併發的狀況下本身就會垮了
若是外部服務特別重要,咱們每每會考慮引入多個同類型的服務,根據價格、服務標準作路由,在出現問題的時候自動降級
架構設計
表的設計
表的設計和Api的定義相似,屬於那種開頭沒有開好,之後改變須要花10x代價的,我知道,一開始在業務不明確的狀況下,設計出良好的一步到位的表結構很困難,可是至少咱們能夠作的是有一個好的標準:
統一的附加字段,create_time,update_time,version等
表的命名標準,好比[domain]_[tablename]_[tabletype]
字段類型、長度標準
雖沒有外鍵,可是外表關聯字段和主表字段的命名標準
_id仍是_no等字段命名的區別
除了標準,儘量須要結合業務以及業務可能的擴展思考一下:
1:N的可能性,是有1就足夠了,仍是一開始就要設計1:N的層次關係
若是表字段可能會不少,業務變化多,是否考慮1:1甚至1:N的擴展表,把擴展字段從主表分開
表的領域職責,表可能也會分上游、中游、下游,什麼字段應該在哪裏過重要了(我以爲表的領域至關於以前提到的項目結構中的包的分類,這個最好一開始定義清楚)
關聯表字段冗餘冗餘到什麼程度,冗餘字段的同步
枚舉的維護方式,是否考慮字典表?
對於表結構文檔,我以爲列出字段類型、長度、說明是不夠的,若是能結合業務代碼梳理清楚下面這些,那這個文檔就是真正有價值的表結構文檔:
記錄由哪一個業務模塊建立
數據重要程度
數據歸檔方案
字段數據源頭
字段會由誰更改
字段可能會在哪裏緩存
我想90%的業務項目都是所謂的三層結構,Web層處理參數調用Service層作Db層的聚合,Db層基本就是代碼生成或Orm框架補充少許的手寫SQL。對於這樣的項目,大部分人認爲是沒有設計的,也不須要設計。我認爲那是由於沒有好好思考:
在咱們寫下if-else的時候,咱們就能夠考慮使用抽象類+具體實現類的方式來替代;
在實現層次化業務處理的時候,就能夠考慮使用Filter或職責鏈模式來實現;
在封裝外部Api的時候與其每次都寫一套解析邏輯,咱們是否考慮進行動態封裝呢;
在數據改變後咱們要記錄改版軌跡,與其複製粘貼是否考慮過發佈訂閱模式;
說白了就是利用各類設計模式和OO思想,來儘量在業務變化須要擴展的時候:
只是新增代碼而不是修改代碼;
儘量減小重複代碼複製粘貼;
儘量讓同類代碼都呆在一塊兒;
儘量讓直筒式的代碼有層次;
在一個公司層面,若是有幾十個,幾百個業務項目,咱們看這個公司的技術水平到了什麼程度,我我的認爲不只僅是用了什麼新技術,而是是否:
具備統一的開發、服務框架
具備統一的運維、監控、中間件、測試平臺
具備清晰的縱向領域劃分
具備清晰的橫向基礎平臺服務和基礎業務服務
具備統一的代碼工做模式
最簡單的一個例子,一個業務從前到後跨10個事業部,100個服務,實現灰度測試,想一想這件事情有多難?整個公司層面要實現步調一致的這些東西還確實很難,不只僅是技術能力的體現,沒有良好的組織架構,人心不齊,恐怕這些沒法實現,實現了也沒法推廣,推廣了也沒法持續……固然,這些已經超出我的能作的了,做爲程序員的咱們應該從我作起,認真考慮前面提到的這些問題,至少在項目內部作良好的設計。
再來看看文首的問題,你看,雖然只是寫業務代碼,若是要寫的足夠好,必需要了解設計模式、理解各類彈力設計、理解事務、熟悉框架、瞭解中間件原理,怎麼可能學不到東西,要實現健壯的業務代碼,其實很難,要考慮的東西太多了,若是說寫框架咱們須要考慮不一樣的使用方和使用環境,這很難,寫業務代碼咱們要考慮到千奇百怪的使用行爲,要考慮到層次不起的對接方,這不比寫框架簡單。對於5年+經驗豐富的程序員應當有能力開一個好頭,或者說願意在老代碼上去作一些改變,不然你的價值在哪裏呢?
若是你對技術提高很感興趣,能夠加入Java高級技術來交流學習:856443934,裏面都是同行,有資源分享和技術進階思惟導圖,其中:(分佈式架構、高可擴展、高性能、高並 發、Jvm性能調優、Spring,MyBatis,Nginx源碼分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。歡迎一到五年的工程師加入,合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!
本文只是展開了一些想到的內容,每一點都有不少東西能夠寫,也沒時間一些子展開說太多,這些細節留在從此的文章慢慢展開了。