Hybris平臺Web架構模式演變:前后端分離
“前后端分離”顯然已不是什么新鮮的話題,表面上看是一場架構模式的變革,但實質上是為了解決以往傳統的服務端MVC設計模式的一些詬病和痛點。前后端分離帶來的全新的前后端協作方式能夠讓專業的人做專業的事,無論前端后端都能更專注在自己擅長的方面。那么如何基于一個成熟的Hybris平臺進行前后端分離?接下來,我們將會逐一剖析這個演變過程。
Hybris平臺Web層現狀
眾所周知,Hybris平臺是一套成熟的電商解決方案,當然也包括Web層的定制化。由于Hybris平臺項目至今已經經歷過許多版本,對電商的核心流程進行了不同程度上的重組與優化。對于Web層,相對于核心流程來說,升級的速度和頻率則顯得稍微緩慢。慢慢的我們會發現,在當前前端技術突飛猛進的發展趨勢下,Hybris平臺Web層的技術體系會變的相對陳舊,比如:缺少了動靜分離。因此,在這個技術體系下進行Web層的二次研發,往往還面臨著前后端耦合依賴的局面,導致項目進度緩慢。
Hybris平臺Web層架構的不足
Hybris平臺Web層基于傳統的服務端MVC(Model-View-Controller)設計模式,傳統的服務端MVC架構在為我們提供優勢的同時也會帶來以下不足:
- 對于簡單的頁面,嚴格遵循MVC,會增加結構的復雜性,并可能產生過多的更新操作,降低運行效率
- 視圖與控制器之間過于緊密的連接。視圖沒有控制器的存在,其應用是很有限的,反之亦然,這樣就妨礙了他們的獨立重用。
- View的渲染通過服務端完成,最終呈現給瀏覽器的是帶有Model的視圖頁面,性能無法得到很好的優化
- 控制器會變得復雜,很多人會在Controller(Spring),Action(Struts)中寫業務代碼已經變得很常見,所有的操作都在控制器中,導致業務與控制器相耦合
- 對象間接地通過控制器耦合在一起,一個對象在控制器中查詢獲得,然后復制給另外一個對象,這兩個對象就耦合在一起
- View通過服務端完成后,視圖頁面包含CSS, JS等資源,這些資源需要重新請求(雖然可能已經進行了緩存)
Hybris平臺前后端協作的痛點
由于Hybris平臺基于傳統的服務端MVC(Model-View-Controller),在這個模式下,前后端協作模式一般采用以下兩種方式:
- 前端直接在服務端View中編寫模版,這樣做的問題在于,編寫的過程中強依賴服務端環境。在服務端沒有完成的情況下,前端無法進行完整測試
- 前端先編寫靜態原型,完成后,后端在View中套用靜態原型。這樣做的問題在于,服務端需要對前端代碼進行瀏覽,以免出錯
這兩種協作方式都存在問題。
在模式一下,前端必須掌握一定的服務端JSP技術,并且還需要對Hybris平臺內部View的劃分機制有一定的了解。因此,學習成本是我們第一要面對的問題。此外,視圖的測試依賴服務端環境,導致測試滯后。
在模式二下,首先帶來的是時間、成本的消耗,靜態原型對于用戶來說不是最終的產品,只是開發過程中的一個過渡品。其二,將靜態原型轉換到服務端View的過程中,后端開發人員需要了解前端的設計,比如:如何找到正確的切分點將一個完整的靜態頁面切分成若干小的片斷,并應用于服務端。在比如:如何正確地使用前端的第三方庫?這些問題,不同的前端技術有著不同的做法,因此當后端開發人員在套用靜態原型的過程中,無形當中增加了了解,學習前端技術的成本。其三,在靜態原型沒有完成情況下,如果服務端開發工作已經完畢,那么前后端集成工作則處于等待狀態,造成集成緩慢。
如何解決這些不足,并消除痛點呢? 這就是我們近期在使用傳統技術經過搭建了幾十個Hybris電商運營網站之后進行的一次大膽的前后端分離的嘗試,取得了很好的效果。下面,將為讀者分享Hybris平臺Web架構模式演變及并行化實踐。
Hybris平臺Web前后端分離
Web架構
上圖可見,我們將View、Controller從傳統的服務端MVC架構中遷移到客戶端。客戶端負責視圖的渲染,交互的控制。數據的獲取通過Restful API接口使用JSON格式交互。而后端只需要負責業務邏輯,數據的存儲,數據模型的定義,并為前端提供JSON格式的數據。
這樣改變之后,頁面的渲染完全從服務端分離出來,并且渲染之后的后續交互,數據都交由客戶端代碼完成。在這樣的架構模式下,前端代碼倉庫與后端隔離,前端獨立負責靜態資源,View模板的維護和發布。
代碼組織方式
前后端未分離:傳統的服務端MVC架構下,前后端代碼放置在同一個代碼倉庫中,前端開發過程中需要導入整個代碼倉庫,并且很難獨立部署與運行。
前后端分離:前后端代碼庫分離,前端代碼可以進行數據的本地化Mock,因此前端可獨立開發和測試,以及部署。而后端 代碼中除了功能實現外,還有著詳細的測試用例,以保證API的可用性,降低集成風險。
帶來的挑戰
新的Web架構在給前端帶來更多的便利性的同時,也同樣帶來了不小的挑戰。比如,如何繼續遵循Hybris平臺后端開發最佳實踐,如何統一進行JSON數據轉換等等一系列的問題,在我們開發過程中一一浮出水面。有些是在做出這種架構選擇時就預見到的,有些是在具體實施中遇到的。
1.前端技術選型
既然客戶端負責View,我們就需要選擇一種適合的前端框架來滿足要求,從而丟棄傳統的Hybris平臺前端框架JQuery. 在進行斟酌后,對于前端技術采用如下:
ECMAScript + React + Node + NPM+ Typescript
其中React最為核心,它不但提供了虛擬DOM機制,并且組件化的開發思想使的頁面結構化合理。前端開發可以完全關注到組件的開發中,進行模板的編寫、數據的綁定、事件的處理。開發過程中不會受到后端的影響,順利完成本地化測試。
2.遵循Hybris平臺后端開發最佳實踐
Hybris平臺對于每一個頁面請求,都有與之對應的控制器,并且平臺的Web層基于Spring MVC框架,利用這些特點,我們決定將每一個OOTB 控制器包裝成為一個完成數據交互的Endpoint,從而為前端提供Rest風格的API接口。與此同時,在Hybris平臺內部,仍然采用DTO(Data Transfer Object )作為API服務層與Fa?ade層之間的數據傳輸對象,平臺內部的數據轉換過程不需要發生任何改變,仍然采用Converter/ Populator機制。
3.JSON數據轉換
正如上文提及,每一個頁面請求,都有與之對應的控制器。比如:購物車頁面對應CartPageController,產品分類頁面對應CategoryPageController,當瀏覽器根據不同的頁面完成PageLoad后,將會觸發控制器中默認的Get方法中,通過該方法為視圖提供所需要的數據。由于在Rest API層面上,我們仍然采用DTO作為數據載體,可見,在這些默認的Rest Get方法中,將會侵入一些代碼片斷用來完成DTO到JSON的轉換。顯而易見,如果在每一個默認的Rest Get方法中都加入轉換代碼不是一個很好的處理方式,會造成代碼的過渡重復,產生壞味道。那么,我們如何從原始的代碼中將數據轉換的過程分離出來呢?我們采用了BeforeViewHandler攔截器。
優點:
- 攔截器可以自動攔截到需要進行數據轉換的Rest Get 請求
- 數據轉換代碼與控制器解偶,通過攔截器完成
- 易于配置管理
4.組件中的JSON處理
對于可以重復使用的頁面片斷,Hybris平臺采用組件的方式進行處理。比如:Global Header, Global Footer. 在前后端分離的架構下,客戶端同樣負責View的渲染,那么Hybris 的組件又該如何為前端提供所需要的數據呢?Hybris允許為組件配置對應的控制器,當一個請求導向至組件時,對應的控制器將會自動觸發。但是由于Hybris對于組件控制器的管理與常規有所不同,在受置于其的約束下,我們無法使用BeforeViewHandler攔截器來處理,因此,采用JSON-taglib為前端View提供JSON數據則成為一種可選方案。
提供安全機制
前后端分離的Web架構中,如何解決交互過程中產生的安全性風險是需要考慮的另一個問題。
1.基于Hybris平臺自身的特性,通過Spring Form 提交的表單,自身已經加入CSRF Token 校驗機制。那么如何在HTML Form提交的過程中避免CSRF攻擊呢?
解決方式:
- 利用Hybris OOTB 服務生成CSRF Token, 并將 Token返回前端
- 在每一次提交過程中,Token會作為數據的一部分提交給后端
- 利用Hybris OOTB CSRF校驗機制進行Token的驗證
2.敏感數據的加密處理
在數據傳輸的過程中,我們需要對敏感數據進行必要的加密處理,避免明文數據的傳輸,減少非正常請求的攻擊。
解決方式:
- Hybris通過RSA生成公鑰,私鑰(公鑰/私鑰是基于1024+ Base64S 加密),并將公鑰返回前端
- 前端使用公鑰以及RSA client lib進行加密,并把加密后的數據傳遞給Hybris。此時在網絡傳輸過程中,使用的是加密后的數據
- Hybris 使用私鑰進行解密后,再使用必要的敏感數據
3.Restful API的安全機制
對于Restful API的調用,采用授權認證的安全機制來約束匿名與非匿名請求。比如,對于下訂單流程中所暴露的API,必須是登錄成功后的用戶才能訪問,限制匿名用戶請求。
解決方式:
- 采用注解方式,顯示地聲明一個Rest API為Require Hard LogIn
性能考慮
在前后端分離的架構模式下,前端有且僅有靜態內容。View視圖的渲染脫離服務端,不需要任何服務端技術進行動態化組裝,因此對于前端資源可以考慮CDN加速,緩存機制,從而提高性能。
由于前端內容是完全的靜態內容,在初次獲取以后的大部分時間內,瀏覽器使用的就是本地緩存,也就是說,服務器的壓力主要來自于承載數據的Restful API調用。因此,合理的對象創建,以及業務邏輯的優化能夠幫助我們減少性能的開銷。比如:
1.減少DTO對象的創建
- 對于一個頁面所需要的數據,盡量一次性提供完畢,減少DTO的多次創建,使DTO到JSON的轉換只發生一次,從而減少數據轉換帶來的性能開銷
2.攔截器最小化配置
- 對于BeforeViewHandler,刪除不需要監聽的請求,從而減少BeforeViewHandler內部流程,提高代碼性能
最后,對于電商網站來說,圖片資源的管理與使用同樣是性能的一個考核指標。在這里,我們對于圖片資源的規劃采用的方式是:將圖片實體獨立出Hybris 電商文件系統。即Hybris電商文件系統不存儲任何圖片實體,通過OOTB Media對象保存產品或者內容與圖片的映射關系。真正的圖片實體則被保存在其他第三方系統,例如:Scene7,Amazon S3。由于Hybris 只保存映射關系,簡單來說就是資源的URL,并通過Restful API將URL返回給前端。那么當請求訪問一個具體的圖片時,前端可采用懶加載的機制,根據需要才將圖片URL賦予SRC屬性,從而提高前端性能,減輕服務端負擔,提高頁面的加載速度。此外,緩存的合理使用同樣也是提高性能的一種手段。
前后端調用流程
如上圖所示(Promotion detail為例),一套完整的渲染流程包含以上步驟,其中不乏一些技巧,比如:前端如何利用Hybris Page Type屬性來完成JS/CSS文件的查找,從而避免不相關JS/CSS文件的加載。關鍵偽代碼參考如下:
- 利用Hybris OOTB Page type屬性動態加載對應JS/CSS文件
If “empty pageType” <script type="text/javascript" src="XXXXXXXXXX/js/static.bundle.js?v={assetVersion}"></script> <link href=" XXXXXXXXXX /css/static.css?v={assetVersion}" rel="stylesheet"> Else <script type="text/javascript" src=" XXXXXXXXXX /js/pageType.bundle.js?v={assetVersion}"></script> <link href=" XXXXXXXXXX /css/pageType.css?v={assetVersion}" rel="stylesheet">
- 統一的export function處理JSON數據以及Mock數據的切換,其他組件只需import即可
- BeforeViewHandler攔截器定義與配置
- JSON數據轉換
結束語
前后端分離的Web架構使得前后端職責更加明確。清晰的分工,可以讓開發并行,減少相互依賴,提高開發效率。View的渲染來自于客戶端,性能上得到進一步的提升。部署相對獨立,很好地應對了復雜多變的前端需求。同時,前后端分離后,應用代碼不再是前后端耦合,只有在運行期才會有調用依賴關系,易于管理與維護。最后,基于Hybris平臺的前后端分離的Web架構模式同樣易于向SPA Web應用轉型,帶來更快,更好的用戶體驗。
作者:楊智,現就職于奧博杰天軟件有限公司,擔任多個電子商務項目的解決方案架構師。曾擔任未來國際軟件股份有限公司多個項目的技術負責人,負責政府網站以及政務平臺設計,研發。參與國家林業局多個系統集成項目。 關注電子商務應用,微服務架構以及DevOps。
來自:http://blog.csdn.net/dev_csdn/article/details/79415323