Midway-ModelProxy — 輕量級的接口配置建模框架

前言

使用Node做前後端分離的開發模式帶來了一些性能及開發流程上的優勢(見《前後端分離的思考與實踐 一》), 但同時也面臨不少挑戰。在淘寶複雜的業務及技術架構下,後端必須依賴Java搭建基礎架構,同時提供相關業務接口供前端使用。Node在整個環境中最重要的工作之一就是代理這些業務接口,以方便前端(Node端和瀏覽器端)整合數據做頁面渲染。如何做好代理工作,使得前後端開發分離之後,仍然可以在流程上無縫銜接,是我們需要考慮的問題。本文將就該問題做相關探討,並提出解決方案。

由於後端提供的接口方式可能多種多樣,同時開發人員在編寫Node端代碼訪問這些接口的方式也有可能多種多樣。如果我們在接口訪問方式及使用上不做統一架構處理,則會帶來以下一些問題:

1. 每一個開發人員使用各自的代碼風格編寫接口訪問代碼,造成工程目錄及編碼風格混亂,維護相對困難。

2. 每一個開發人員編寫自己的mock數據方式,開發完畢之後,需要手工修改代碼移除mock。

3. 每一個開發人員爲了實現接口的不同環境切換(日常,預發,線上),可能各自維護了一些配置文件。

4. 數據接口調用方式無法被各個業務model非常方便地複用。

5. 對於數據接口的描述約定散落在代碼的各個角落,有可能跟後端人員約定的接口文檔不一致。

6. 整個項目分離開發之後,對於接口的聯調或者測試迴歸成本依然很高,需要涉及到每一個接口提供者和使用者。

於是我們希望有這樣一個框架,通過該框架提供的機制去描述工程項目中依賴的所有外部接口,對他們進行統一管理,同時提供靈活的接口建模及調用方式,並且提供便捷的線上環境和生產環境切換方法,使前後端開發無縫結合。ModelProxy就是滿足這樣要求的輕量級框架,它是Midway Framework 核心構件之一,也可以單獨使用。使用ModelProxy可以帶來如下優點:

1. 不同的開發者對於接口訪問代碼編寫方式統一,含義清晰,降低維護難度。

2. 框架內部採用工廠+單例模式,實現接口一次配置多次複用。並且開發者可以隨意定製組裝自己的業務Model(依賴注入)。

3. 可以非常方便地實現線上,日常,預發環境的切換。

4. 內置river-mockmockjs等mock引擎,提供mock數據非常方便。

5. 使用接口配置文件,對接口的依賴描述做統一的管理,避免散落在各個代碼之中。

6. 支持瀏覽器端共享Model,瀏覽器端可以使用它做前端數據渲染。整個代理過程對瀏覽器透明。

7. 接口配置文件本身是結構化的描述文檔,可以使用river工具集合,自動生成文檔。也可使用它做相關自動化接口測試,使整個開發過程形成一個閉環。

ModelProxy工作原理圖及相關開發過程圖覽

在上圖中,開發者首先需要將工程項目中所有依賴的後端接口描述,按照指定的json格式,寫入interface.json配置文件。必要時,需要對每個接口編寫一個規則文件,也即圖中interface rules部分。該規則文件用於在開發階段mock數據或者在聯調階段使用River工具集去驗證接口。規則文件的內容取決於採用哪一種mock引擎(比如 mockjs, river-mock 等等)。配置完成之後,即可在代碼中按照自己的需求創建自己的業務model。

下面是一個簡單的例子:

【例一】

  • 第一步 在工程目錄中創建接口配置文件interface.json, 並在其中添加主搜接口json定義
  1. {  
  2.     "title""pad淘寶項目數據接口集合定義",  
  3.     "version""1.0.0",  
  4.     "engine""mockjs",  
  5.     "rulebase""./interfaceRules/",  
  6.     "status""online",  
  7.     "interfaces": [ {  
  8.         "name""主搜索接口",  
  9.         "id""Search.getItems",  
  10.         "urls": {  
  11.             "online""http://s.m.taobao.com/client/search.do" 
  12.         }  
  13.     } ]  
  • 第二步 在代碼中創建並使用model
  1. // 引入模塊  
  2. var ModelProxy = require( 'modelproxy' );   
  3.  
  4. // 全局初始化引入接口配置文件  (注意:初始化工作有且只有一次)  
  5. ModelProxy.init( './interface.json' );  
  6.  
  7. // 創建model 更多創建模式請參後文  
  8. var searchModel = new ModelProxy( {  
  9.     searchItems: 'Search.getItems'  // 自定義方法名: 配置文件中的定義的接口ID  
  10. } );  
  11.  
  12. // 使用model, 注意: 調用方法所需要的參數即爲實際接口所需要的參數。  
  13. searchModel.searchItems( { q: 'iphone6' } )  
  14.     // !注意 必須調用 done 方法指定回調函數,來取得上面異步調用searchItems獲得的數據!  
  15.     .done( function( data ) {  
  16.         console.log( data );  
  17.     } )  
  18.     .error( function( err ) {  
  19.         console.log( err );  
  20.     } );  

ModelProxy的功能豐富性在於它支持各種形式的profile以創建需要業務model:

  • 使用接口ID創建>生成的對象會取ID最後’.'號後面的單詞作爲方法名
  1. ModelProxy.create( 'Search.getItem' ); 

使用鍵值JSON對象>自定義方法名: 接口ID

  1. ModelProxy.create( {  
  2.     getName: 'Session.getUserName',  
  3.     getMyCarts: 'Cart.getCarts' 
  4. } );  
  • 使用數組形式>取最後 . 號後面的單詞作爲方法名

下例中生成的方法調用名依次爲: Cart_getItem, getItem, suggest, getName

  1. ModelProxy.create( [ 'Cart.getItem''Search.getItem''Search.suggest''Session.User.getName' ] ); 
  • 前綴形式>所有滿足前綴的接口ID會被引入對象,並取其後半部分作爲方法名
  1. ModelProxy.create( 'Search.*' ); 

同時,使用這些Model,你可以很輕易地實現合併請求或者依賴請求,並做相關模板渲染

【例二】 合併請求

  1. var model = new ModelProxy( 'Search.*' );  
  2.  
  3. // 合併請求 (下面調用的model方法除done之外,皆爲配置接口id時指定)  
  4. model.suggest( { q: '女' } )  
  5.     .list( { keyword: 'iphone6' } )  
  6.     .getNav( { key: '流行服裝' } )  
  7.     .done( function( data1, data2, data3 ) {  
  8.         // 參數順序與方法調用順序一致  
  9.         console.log( data1, data2, data3 );  
  10.     } );  

【例三】 依賴請求

  1. var model = new ModelProxy( {  
  2.     getUser: 'Session.getUser',  
  3.     getMyOrderList: 'Order.getOrder' 
  4. } );  
  5. // 先獲得用戶id,然後再根據id號獲得訂單列表  
  6. model.getUser( { sid: 'fdkaldjfgsakls0322yf8' } )  
  7.     .done( function( data ) {  
  8.         var uid = data.uid;  
  9.         // 二次數據請求依賴第一次取得的id號  
  10.         this.getMyOrderList( { id: uid } )  
  11.             .done( function( data ) {  
  12.                 console.log( data );  
  13.             } );  
  14.     } );  

此外ModelProxy不僅在Node端可以使用,也可以在瀏覽器端使用。只需要在頁面中引入官方包提供的modelproxy-client.js即可。

【例四】瀏覽器端使用ModelProxy

  1. <!-- 引入modelproxy模塊,該模塊本身是由KISSY封裝的標準模塊--> 
  2. <script src="modelproxy-client.js" ></script> 
  1. <script type="text/javascript">  
  2.     KISSY.use( "modelproxy"function( S, ModelProxy ) {  
  3.         // !配置基礎路徑,該路徑與第二步中配置的攔截路徑一致!  
  4.         // 且全局配置有且只有一次!  
  5.         ModelProxy.configBase( '/model/' );  
  6.  
  7.         // 創建model  
  8.         var searchModel = ModelProxy.create( 'Search.*' );  
  9.         searchModel  
  10.             .list( { q: 'ihpone6' } )  
  11.             .list( { q: '衝鋒衣' } )  
  12.             .suggest( { q: 'i' } )  
  13.             .getNav( { q: '滑板' } )  
  14.             .done( function( data1, data2, data3, data4 ) {  
  15.                 console.log( {  
  16.                     "list_ihpone6": data1,  
  17.                     "list_衝鋒衣": data2,  
  18.                     "suggest_i": data3,  
  19.                     "getNav_滑板": data4  
  20.                 } );  
  21.             } );  
  22.     } );  
  23. </script>  

同時,ModelProxy可以配合Midway另一核心組件Midway-XTPL一起使用,實現數據和模板以及相關渲染過程在瀏覽器端和服務器端的全共享。關於ModelProxy的詳細教程及文檔請移步https://github.com/purejs/modelproxy

總結

ModelProxy以一種配置化的輕量級框架存在,提供友好的接口model組裝及使用方式,同時很好的解決前後端開發模式分離中的接口使用規範問題。在整個項目開發過程中,接口始終只需要定義描述一次,前端開發人員即可引用,同時使用River工具自動生成文檔,形成與後端開發人員的契約,並做相關自動化測試,極大地優化了整個軟件工程開發過程。

【注】River 是阿里集團研發的前後端統一接口規範及相關工具集合的統稱

原文鏈接:http://ued.taobao.org/blog/2014/04/modelproxy/

【編輯推薦】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章