前后端分離
按照現在的趨勢,前后端分離幾乎已經是業界對開發和部署方式所達成的一種共識。所謂的前后端分離,並不是傳統行業中的按部門划分,一部分人只做前端(HTML/CSS/JavaScript等等),另一部分人只做后端(或者叫服務端),因為這種方式是不工作的:比如很多團隊采取了后端的模板技術(JSP, FreeMarker, ERB等等),前端的開發和調試需要一個后台Web容器的支持,從而無法將前后端開發和部署做到真正的分離。
通常,前后端分別有着自己的開發流程,構建工具,測試等。做前端的誰也不會想要用Maven或者Gradle作為構建工具,同樣的道理,做后端的誰也不會想要用Grunt或者Gulp作為構建工具。前后端僅僅通過接口來協作,這個接口可能是JSON格式的RESTFul的接口,也可能是XML的,重點是后台只負責數據的提供和計算,而完全不處理展現。而前端則負責拿到數據,組織數據並展現的工作。這樣結構清晰,關注點分離,前后端會變得相對獨立並松耦合。但是這種想法依然還是很理想化,前后端集成往往還是一個很頭痛的問題。比如在最后需要集成的時候,我們才發現最開始商量好的數據結構發生了變化,而且這種變化往往是在所難免的,這樣就會增加大量的集成時間。
歸根結底,還是前端或者后端感知到變化的時間周期太長,不能“及時協商,盡早解決”,最終導致集中爆發。怎么解決這個問題呢?我們需要提前協商好一些契約,並將這些契約作為可以被測試的中間產品,然后前后端都通過自動化測試來檢驗這些契約,一旦契約發生變化,測試就會失敗。這樣,每個失敗的測試都會驅動雙方再次協商,有效的縮短了反饋周期,並且降低集成風險。具體的實踐方式,請參加我同事的一篇博文,“前后端分離了,然后呢?”http://icodeit.org/2015/06/whats-next-after-separate-frontend-and-backend/。
不過,僅僅靠紀律是不夠的,還需要通過工具的輔助來提高效率。下面,我們就來看一下,一個API設計工具——Swagger,將如何幫助我們更好的實現“前后端分離”。
Swagger
Swagger包括庫、編輯器、代碼生成器等很多部分,這里我們主要講一下Swagger Editor。這是一個完全開源的項目,並且它也是一個基於Angular的成功案例,我們可以下載源碼並自己部署它,也可以修改它或集成到我們自己的軟件中。
在Swagger Editor中,我們可以基於YAML語法定義我們的RESTful API,然后它會自動生成一篇排版優美的API文檔,並且提供實時預覽。相信大多數朋友都遇到過這樣一個場景:明明調用的是之前約定好的API,拿到的結果卻不是想要的。可能因為是有人修改了API的接口,卻忘了更新文檔;或者是文檔更新的不及時;又或者是文檔寫的有歧義,大家的理解各不相同。總之,讓API文檔總是與API定義同步更新,是一件非常有價值的事。下面我們通過一個例子來感受一下Swagger給我們帶來的好處。
首先我們需要安裝一個Swagger Editor,或者也可以直接使用在線版本http://editor.swagger.io/。如果需要在本地啟動編輯器,執行以下三行命令即可(前提是已經安裝好了Node.js):
git clone https://github.com/swagger-api/swagger-editor.git
cd swagger-editor
npm start
當我們修改了API的定義之后,在編輯器右側就可以看到相應的API文檔了,而且永遠是最新的。
不僅如此,它還能夠自動生成Mock server所需要的代碼,這樣一來前端開發就再也不用等着后端API 的實現了。除此之外,它還有一個更強大的功能,甚至能夠幫助我們自動生成不同語言的客戶端的代碼。Swagger是基於插件來實現各種不同的語言的,所以,如果已經提供的語言中沒有你正在用的,你也可以自己實現相應的插件,甚至是從源代碼級別進行定制化。
契約測試
談到了前后端分離,那么在所難免,會遇到一些集成的問題:一撥人在全心全意的進行前端開發,另一撥人在心無旁騖的做后端開發,那么誰應該為集成買單呢?在現在這個持續集成、持續交付的年代里,我們應該如何去保證雙方不會分道揚鑣、越走越遠呢?
所以,在一開始就定一個契約就成了迫在眉睫的事情,雙方就API相關的內容,包括路徑、參數、類型等達成一致,當然,這份契約並不是一旦創建就不能修改的,而且,如果一開始沒有設計好,很有可能會頻繁的修改。這個時候,要讓雙方都能夠實時的跟蹤最新的API就成了一個難題。還好,在總結了前人的經驗和教訓之后,我們早已有了應對之策,那就是契約測試
。
老馬(Martin Fowler)早在2011年的時候就發表了一篇博客http://martinfowler.com/bliki/IntegrationContractTest.html,專門討論了如何做契約測試。
首先,我們先假設我們已經有了一份契約,可能是基於JSON格式的,有可能是基於XML格式的,這都不重要。然后,前端會根據這份契約建立一個Mock server,所有的測試都發往這個Mock server。有兩方面的原因:一是這個時候可能后台的API還沒有開發完成;二是有可能因為網絡等其他方面的原因導致直接調用真實的后台API會很不穩定或者很耗時。到這里,可能有人就要說了,如果后台的API實現和之前約定的並不一樣,怎么能保證到了集成的時候雙方還能很順利的集成呢?其實這個問題並不難,只需要讓前端的測試定期連接真實的API執行一遍就能盡早的發現差異性。比方說,在我們平常的build pipeline上添加一個job,讓這些測試每天在午夜里連着真實的API執行。如果,第二天發現這些測試有的失敗了,那么就需要和開發后台API的人員進行一次溝通了,很有可能由於真實的業務邏輯發生了變化,API在實現的時候,已經和之前的契約不一致了,如果是這樣,那么相應的測試和契約定義就需要更新以滿足最新的業務需求。
總之,進行契約測試的目的就是盡早的發現差異性,並作出調整,將最后集成的風險降到最低。