創業之初,咱們每每會爲了快速迭代出產品,而選擇最簡單的技術架構,好比LAMP架構,SSH三層架構。這些架構能夠適應初期業務的快速發展,可是,隨着業務變得愈來愈複雜,咱們會發現這些架構愈來愈難支撐業務的發展,出如今一個類中寫好幾千行代碼,一個方法中處處都是if else語句,若是中間遇到主程序猿離職,後面介入的程序猿幾乎沒法理解這些代碼,到最後,產品愈來愈難迭代,只能推翻重作。若是咱們在創業初始就以一種適應性較強的架構去寫代碼,後面就會少走不少彎路。下面的文章是我本身總結出來的一套架構,通過實踐,適應性還算不錯。html
總的來講個人通用架構仍是以三層架構爲基礎進行演變的,在經典的三層架構中,最上層的是controller,中間是service,下層是dao。在個人架構中,最上層是網關層,controller只是網關的一種,中間是業務層,service只是業務層的入口,最下層是基礎層,dao只是基礎層中的數據存儲組件。redis
2.一、網關層spring
網關層本質上是對不一樣的網絡協議的請求進行處理,好比HTTP協議,TCP協議,固然,也能夠對其餘協議進行處理。具體見下圖:數據庫
2.1.一、HTTP請求編程
通常來自PC端和APP端的請求都是基於HTTP協議的,對於處理HTTP請求的方案,業內已經很是成熟了。首先,tomcat容器自己已經把HTTP請求處理的複雜性封裝掉了,其次,spring mvc對請求處理提供了RESTful風格的編碼方式,大大下降了開發的複雜度。咱們要作的就是對controller按照業務領域劃分,好比按照訂單、會員去劃分大的領域,裏面的各類方法就是這個領域內的操做。這裏的controller就是統一網關處理層,對於每一個controller的方法只作三件事,第一,將請求參數解析出來並組裝成內部參數,第二調用下層服務執行業務邏輯,第三組裝返回結果,對於異常狀況,須要記錄異常堆棧日誌並轉換錯誤碼,堆棧信息不要暴露到調用方。數組
2.1.二、TCP請求緩存
對於處理TCP請求的方案,業內也已經很成熟了,好比Netty。可是,TCP請求畢竟太底層,咱們每每會基於TCP協議去開發本身的協議。另外,不少分佈式框架都是基於TCP協議的,好比RPC框架Dubbo,消息框架RocketMQ等等。從單機系統到分佈式系統,無非就是網關層多了處理TCP請求的邏輯,理論上底層的業務是無需感知本身究竟是出於單機環境仍是分佈式環境,網關層的做用就是要屏蔽這種不一樣外部調用源的細節。在Dubbo服務端中,咱們須要實現遠程接口,並對遠程服務調用進行內部的轉發,轉發的邏輯也很簡單,首先是解析參數並組裝內部參數,而後調用業務層的接口執行業務邏輯,最後組裝返回結果,對於異常處理也須要在這裏作掉,防止異常暴露給外部應用。tomcat
2.1.三、小結網絡
網關層本質是對協議進行處理,同時將業務邏輯收斂到網關層,而不是暴露給外部,當內部業務邏輯進行重構的時候,外部調用方就不須要感知這些變化,當外部調用源增長時,內部業務邏輯不須要感知這種變化,從而將外部調用方和內部業務邏輯進行了解耦。架構
**
**
業務層是一個系統,不管是單機系統仍是分佈式系統羣中的某個業務系統,業務層都是承載業務流程和規則的地方。業務層從外到內包含三層:第一層是業務服務,第二層是業務流程,第三層是業務組件。具體以下圖:
2.2.一、業務服務
業務服務是業務層對外的統一門面,它由三方面組成:業務接口、入參、出參。
a) 業務接口
一個業務接口表明一個領域的業務服務,好比訂單域的業務服務就由接口OrderService表示,會員域的業務服務就由接口MemberService表示。接口能夠按照執行性質分爲讀接口和寫接口,好比OrderReadService和OrderWriteService。讀寫分離的好處是能夠對集羣進行讀寫分組,從而管理流量,固然,單機系統讀寫分離意義不是太大。領域內的操做則以業務接口中的方法的形式體現,好比訂單域有下單createOrder,取消訂單cancelOrder等等操做。對於這些操做,儘可能設計出有業務含義的方法,而不是增刪改查,固然,對於一些簡單的業務,也只能增刪改查。
b)入參
接下來,是入參的設計。入參對於讀方法,比較簡單,不作討論。對於寫方法,咱們將入參設計成有層次的數據模型。首先須要設計出公共的數據模型,好比訂單數據模型,商家數據模型,商品數據模型等,而後將這些數據模型和一些特定業務下的個性數據結合,組成Request對象,這個request對象按照不一樣業務操做不一樣而不一樣,對應的返回結果就是response,它也是隨着不一樣業務返回的參數不一樣。
舉個例子,拿下餐飲訂單來講,首先,咱們應該識別出這些業務流程中一些比較基礎的數據模型,好比餐飲領域的菜品、桌位等,這些模型之因此說是基礎模型,是由於,無論下什麼餐飲訂單,菜品和桌位確定是逃不了的,它們是能夠被複用的!所以,咱們分別爲這些基礎模型設計相對於的DO(Domian Object):DishDO(菜品)、BoardDO(桌位)等等,接下來,咱們爲下餐飲訂單設計一個請求對象DishOrderCreateRequest其中DishOrderCreateRequest內部包含了DishDO和BoardDO,另外會包含一些特定的屬性,好比人數啊,折扣啊等等,這樣一來就能作到通用和靈活兼顧,DishOrderCreateRequest表明的個性化的靈活的業務入參,而DishDO和BoardDO等則表明了不易變化的基礎模型。
c) 出參
最後,是出參的設計。對於寫方法,通常出參比較簡單。對於讀方法,出參每每是一個結構與層次比較複雜的組合對象。好比查詢一個訂單,這個訂單有訂單基本信息,還有商品信息,收貨人地址信息等。在設計出參的時候,結構上要設計成組合對象,可是真正查詢的時候,經過查詢選擇器,去查詢不一樣的組合對象。好比查詢選擇器設置商品查詢爲true,地址查詢爲false,那麼此次查詢出的訂單就只包含商品,而不包含地址。
2.2.二、業務流程
業務流程其實就是對業務規則的解釋,只是這種解釋使用代碼去實現的,咱們要作的其實就是準確翻譯這些業務規則,並維護好這些業務規則。
業務流程中能夠大體分爲三種動做節點,一、組裝參數節點 二、規則判斷節點 三、執行動做節點,其中每一個動做節點都是一些業務代碼的片斷。舉個例子,下餐飲訂單,咱們第一步就是將上層傳入的參數組裝出一個基礎的DishOrderDO(組裝參數節點),而後按照特定的規則去填充這個DishOrderDO(規則判斷節點),而後就是調用DAO去建立DishOrderDO(執行動做節點)。
業務流程是最容易變化的地方,要想維護好業務流程並不容易,總的思想是將大的業務流程拆分紅小的業務流程,抽出每一個業務流程中共有的代碼片斷,變成可維護的業務組件。
2.2.二、業務組件
a) 基礎組件
業務組件實際上是將一些內聚的可複用的代碼片斷進行封裝。和業務流程中的三種業務節點相對應,業務組件也分爲三種:組裝參數組件 、規則判斷組件 、動做執行業務組件。業務組件的抽象每每是對業務有了深入理解以後才進行的,盲目地進行業務組件的抽象,每每到頭來白忙活。
b) 能力
對業務組件進行進一步抽象,能夠獲得能力。業務能力是具備必定複用性的組件的組合,好比發短信能力=組裝短信參數組件+發短信組件。對於發短信能力,能夠被不一樣的業務流程複用,好比訂單下單成功發短信,支付成功發短信,邏輯都是類似的,只有內容不一樣。能力是一種粒度比較大的組件,粒度越大,每每複用性就越小,對能力的抽取,也是基於對特定業務深入的理解,沒有一勞永逸的銀彈。
c)更高緯度的抽象
通過本人的實踐,對於互聯網這樣的需求變化極快的場景,更高緯度的組件抽象每每性價比很低,不建議你們去作。
2.三、基礎層
基礎層包含兩個部分,第一是接口定義,第二是技術組件。
加粗文字
2.3.一、接口定義
接口定義是按照不一樣的技術框架,同時結合業務須要,設計出合理的接口,對於業務組件來講,它們只會感知技術接口,而不會去感知技術實現,咱們也不該該將具體的技術細節向上暴露,這也就是所謂的面向接口編程。技術接口每每是業務與技術之間的橋樑,接口自己是含有業務含義的,最多見的就是DAO接口,咱們設計DAO接口的時候,不會設計成insert、update、query這樣業務無關的接口,而是設計成insertUser,updateUserById等等和業務相關的接口,一樣的道理,設計緩存接口的時候,也不能設計成put、get這樣的接口,而應該設計成cacheUser,deprecateUser這樣的接口。
2.3.二、技術組件
單機系統的技術組件通常來講分兩種,一種是通用的技術組件,好比:數據存儲、緩存、消息和調度任務、事務、鎖。一種是基礎設施,好比spring容器,tomcat容器。下面稍微談談通用技術組件。
數據存儲:數據存儲包括關係型數據庫、非關係型數據庫以及文件存儲系統。關係型數據庫,好比MySQL,適合存放絕大部分業務數據。非關係型數據庫,好比hbase,能夠存放歷史日誌,也能夠對歷史的MySQL數據進行歸檔。文件存儲系統,通常都是基於Linux文件系統,好比圖片、html文件等等,也有基於HDFS的,用於大數據分析。
緩存:緩存按響應時間分,能夠分爲納秒級緩存,毫秒級緩存和百毫秒級緩存。納秒級緩存就是通常的基於本地內存的緩存,好比encache,毫秒級緩存通常是集中式的內存緩存,好比memcache,因爲訪問時遠程調用,所以響應時間會延長到幾毫秒,百毫秒級緩存通常是集中式可持久化的緩存,好比redis,因爲存在遠程訪問以及緩存擊穿致使的讀取持久化記錄,它的響應時間會更長些,到幾十甚至上百毫秒。單機系統通常用本地內存緩存就夠了,當緩存被擊穿的時候,直接訪問數據庫。
消息和調度任務:消息和調度任務本質都是一種異步化的手段,區別在於消息沒法控制異步的時間,而調度任務能夠。通常,消息發送出去後,監聽消息的系統會當即收到消息,從而當即觸發業務邏輯的執行,而調度任務則會按照調度規則,一次或者屢次的執行業務邏輯。單機系統中消息和調度任務用到的比較少,在作日誌監控的時候可能會用到消息,在進行數據報表統計的時候可能會用到調度任務。
事務:事務本質都是基於數據庫去實現的,單機系統的事務就是依賴數據庫的事務,咱們可使用spring-tx的事務模板進行事務操做,在業務邏輯開發中,必定要把握事務的大小,建議把業務比較緊密的一堆數據庫操做放在一個事務裏,不要隨意的爲每一個方法都開啓事務。
鎖:單機系統中主要用到兩種鎖:樂觀鎖和悲觀鎖。樂觀鎖依靠在數據庫的業務表加版本字段來實現,每次更新都會去判斷版本是否變化,若是變化則須要重試,這種鎖的粒度比較小。悲觀鎖是基於JDK的Lock接口的,對一個業務流程進行加鎖和釋放鎖的操做,鎖的粒度比較粗。
**
**
以上是我通過很長一段時間的實踐後摸索出來的業務技術架構,自認爲還算通用,並且可以在必定程度上支撐易變的業務。固然這套架構確定不是銀彈,不可能解決全部業務場景,因此最終仍是須要圍繞到具體的場景加以借鑑。