大規(guī)模的JavaScript: 單一的服務(wù)層
當(dāng)我在開(kāi)始架構(gòu)一個(gè)JS 應(yīng)用的原型時(shí),我總是嘗試把這些組件設(shè)計(jì)成既能被Backbone.js應(yīng)用使用也能被一次性的腳本或者工程使用的結(jié)構(gòu)。這些結(jié)構(gòu)性的組件增長(zhǎng)十分迅速,他們變得十分龐雜。在這篇文章中,我將會(huì)研究后端API服務(wù)層的動(dòng)機(jī),優(yōu)點(diǎn),缺點(diǎn)以及建議或者頭腦風(fēng)暴他們的代替實(shí)現(xiàn)方式。
后端API服務(wù)層
盡管你可能實(shí)現(xiàn)了你的后端API(MVC或者別的方式),但是記憶和重復(fù)實(shí)現(xiàn)API的細(xì)節(jié)是非常笨重的。當(dāng)一個(gè)團(tuán)隊(duì)成員說(shuō)他們開(kāi)發(fā)一些東西需要登錄功能,他們是需要重復(fù)造輪子還是你給他們一個(gè)預(yù)編譯的JS函數(shù)調(diào)用呢?
我希望建立一個(gè)名字為younow.js的JS客戶端來(lái)解決這些問(wèn)題,這將允許任何JS應(yīng)用和我們的后臺(tái)交互。有一個(gè)新增的需求需要登錄功能?不用擔(dān)心! 只要調(diào)用YouNow.Api.login()并且綁定處理函數(shù)就行。在這個(gè)例子中,服務(wù)層有著暴露在YouNow.Api命名空間的登錄函數(shù)。
- YouNow.Api.login()
- .done(function (loginData) {
- // Do what you need with the login data
- })
- .fail(function (errorMsg) {
- // Handle the error as you wish
- });
注意:為了實(shí)現(xiàn)JS服務(wù)層,如果你不喜歡JQuery的回調(diào)模板,你可以替換用于接收成功/失敗回調(diào)的函數(shù)。我個(gè)人是喜歡這樣鏈?zhǔn)?管道的回調(diào)方式和標(biāo)準(zhǔn)接口。對(duì)于不喜歡這種方式的人,我也說(shuō)一句你好。
聽(tīng)起來(lái)不錯(cuò),不是嗎?
優(yōu)點(diǎn):對(duì)于每一個(gè)YouNow.Api命名空間內(nèi)的結(jié)點(diǎn),服務(wù)層younow.js將會(huì)有一個(gè)關(guān)于預(yù)期參數(shù)和被隱藏的復(fù)雜機(jī)制的文檔說(shuō)明。尤其是使用服務(wù) 的用戶不用去擔(dān)心這個(gè)請(qǐng)求是GET還是POST,是通過(guò)CDN返回還是我們直接發(fā)送的,更不用擔(dān)心怎樣去構(gòu)建URL和如何處理jsonp數(shù)據(jù)。對(duì)于一個(gè)單 點(diǎn)來(lái)說(shuō),所有的后臺(tái)交互都是孤立的。
缺點(diǎn):這個(gè)文件的增長(zhǎng)迅速,幾乎超出你的想象。對(duì)于每一次后臺(tái)調(diào)用,我們需要一個(gè)增加新的YouNow.Api的結(jié)點(diǎn)。你可以抽象業(yè)務(wù)到helper函數(shù) 中來(lái)處理響應(yīng),jsonp,cdn地址和$.ajax調(diào)用。然而,對(duì)于一個(gè)應(yīng)用來(lái)說(shuō),這個(gè)文件已經(jīng)達(dá)到40kb了。每一個(gè)應(yīng)用有自己的API結(jié)點(diǎn)集,每一 個(gè)都和younow.js交互。這對(duì)于維護(hù)來(lái)說(shuō)是非常困難的。
現(xiàn)在想象一下,一個(gè)簡(jiǎn)單的小應(yīng)用程序,如媒體播放器( 例如一個(gè)JS的包裝器,JWPlayer的初始器)。它可能需要一個(gè)或兩個(gè)接口(登錄的接口和 檢索廣播信息的接口)。那么它必須下載整個(gè)40KB的數(shù)據(jù)包。
另一種實(shí)現(xiàn)1:為每個(gè)應(yīng)用程序分配一個(gè)服務(wù)層
常規(guī)的服務(wù)層可以是簡(jiǎn)單的輔助函數(shù)的關(guān)鍵集合 (ajax,CDN,約定), 每一個(gè)應(yīng)用程序在加載自身的服務(wù)層函數(shù)都將繼承YouNow.Api命名空間。
好處:這解決了主服務(wù)層不斷膨脹的問(wèn)題。
好處:堅(jiān)持什么時(shí)候以及如何擴(kuò)展一個(gè)應(yīng)用程序的命名空間的原則,該解決方案可以清晰地?cái)U(kuò)展出多種應(yīng)用程序。
缺點(diǎn):每一個(gè)應(yīng)用程序的個(gè)性化服務(wù)層可能變得非常臃腫。
缺點(diǎn):如果兩個(gè)應(yīng)用程序都對(duì)同一個(gè)端點(diǎn)接口有共同的需求時(shí),怎么辦?
- // loginservice.js
- YouNow.Mixins.LoginService = {
- login: function () {},
- logout: function () {}
- };
- // broadcastservice.js
- YouNow.Mixins.BroadcastService = {
- get: function () {},
- delete: function () {}
- };
另一種實(shí)現(xiàn)3:完全脫離服務(wù)層。將交互轉(zhuǎn)移到模塊內(nèi)完成
因?yàn)榉?wù)層的存在,想要和后臺(tái)進(jìn)行交互的Backbone模塊只需要簡(jiǎn)單的調(diào)用YouNow.Api離得函數(shù)就行?,F(xiàn)在這些模塊的實(shí)現(xiàn)是精簡(jiǎn)的,但是你也能夠理解這些模塊其實(shí)不必傳輸自己的數(shù)據(jù)到別的地方。感覺(jué)就像是模塊應(yīng)該擁有那種功能。
完全脫離服務(wù)層,我們將會(huì)有一個(gè)擁有登陸/登出功能的用戶模塊和完全實(shí)現(xiàn)(或許是基于基本模塊的擴(kuò)展,這樣我們能夠脫離CDN和ajax helper)。
優(yōu)點(diǎn):各模塊擁有適當(dāng)?shù)墓δ堋?/p>
缺點(diǎn):隨著時(shí)間推移,越來(lái)越多的函數(shù)充斥著這個(gè)模塊。不是很確定這是否是一個(gè)缺點(diǎn),因?yàn)閷?duì)于“胖模型”來(lái)說(shuō),這也許就是一個(gè)優(yōu)點(diǎn)。
【更新】缺點(diǎn):如果小的應(yīng)用想要實(shí)例化你的模塊現(xiàn)在需要Backbone(或者其他你在使用的框架)和它的依賴模塊,很不幸,將會(huì)為模塊的臃腫付出點(diǎn)代價(jià),但是這可以作為你的系統(tǒng)統(tǒng)一化的一個(gè)折中方案。
任何想要使用登陸/登出功能的應(yīng)用都必須要實(shí)例化一個(gè)用戶模塊。這并不令人討厭。舉個(gè)例子,多媒體播放器應(yīng)用需要登陸一個(gè)用戶。那么現(xiàn)在有一個(gè)你的用戶,是一個(gè)用戶模塊的實(shí)例。現(xiàn)在就可以調(diào)用它的登陸功能了。與之相比,不用去直接調(diào)用YouNow.Api.login()。
優(yōu)點(diǎn):服務(wù)層變成了一些了經(jīng)過(guò)封裝的結(jié)點(diǎn)。數(shù)量的增長(zhǎng)將會(huì)體現(xiàn)在兩個(gè)方面:封裝的數(shù)量和函數(shù)的數(shù)量。單層結(jié)構(gòu)只會(huì)在一個(gè)方面增長(zhǎng),所以這個(gè)方案有更好的伸縮性。
我現(xiàn)在正在想彌補(bǔ)這個(gè)方案的缺點(diǎn),這也成為第三種最好的實(shí)現(xiàn)方式,但是我十分喜歡這種方式;通過(guò)下面的第三種實(shí)現(xiàn)方式,它修復(fù)了模塊的過(guò)度膨脹問(wèn)題。
如果你發(fā)現(xiàn)這個(gè)方法的巨大缺陷,請(qǐng)給我留言。
通過(guò)上面的第二種實(shí)現(xiàn)方式(封裝方式),用戶模塊將不會(huì)擁有登錄/登出功能。反而,它將會(huì)封裝登錄服務(wù),也許使用Cocktail.js——我也是這個(gè)項(xiàng)目的維護(hù)者之一;)。
這可以間接的訪問(wèn)。我的意思是,你查看用戶模塊的定義,找不到登錄/登出方法。那么如何一眼看出用戶模塊有那中功能呢?從技術(shù)上說(shuō),我們傳遞數(shù)據(jù)到另一個(gè)服務(wù),登錄服務(wù)獲得在封裝好的用戶模塊的數(shù)據(jù),這樣就實(shí)現(xiàn)了用戶模塊自己的登錄功能。
什么是最好的解決方案?
我認(rèn)為解決方案其實(shí)就是實(shí)現(xiàn)方式2和3的混合體。
在目前,服務(wù)層是一個(gè)好的想法,但是不能滿足復(fù)雜應(yīng)用的伸縮性。目前,我已經(jīng)遷移到胖模塊的實(shí)現(xiàn)(第三種方式),但是還沒(méi)搭配使用分組封裝(第二種方式)。
或許還有別的實(shí)現(xiàn)思路?
原文鏈接:http://www.oschina.net/translate/large-scale-javascript-a-monolithic-service-layer