天天看點

快速web開發中的前後端架構選型最佳實踐

這個最佳實踐是我目前人在做的一個站點,主要功能:

  1. oauth登入
  2. 釋出文章(我稱為"片段"),片段可以自定義一些和内容有關的名額,如“文中人物:12”。支援自定義排版、插圖、建立相冊。
  3. 多個片段可以組織在一起,形成"事件"。
  4. 任意事件、片段都可以互相标記因果關系。
  5. 可以follow事件或者他人。
  6. 事件和片段模糊搜尋。

我對目前前端架構的觀點已在另一篇文章中講過,這裡隻介紹一下目前實踐的情況。

  1. 使用requirejs做子產品化,上線時用r.js打包。
  2. 使用avalon做資料與視圖渲染架構。
  3. 用page.js來管理路由。
  4. 用harp來做靜态資源伺服器。harp支援less、coffeescript等自動編譯,目前端有css或js請求而相應的檔案不存在時,它就會自動查找同名的less或coffeescript等檔案并編譯輸出。

後端選型經曆了兩周左右,目标無非兩個:開發便捷,适用于生産環境。

我對開發便捷的要求是。

  1. 可簡可繁的路由配置,且非常容易生成REST接口。可棄用架構自帶的視圖層。
  2. 架構有一個良好的資料庫層,可以是ORM也可以是AR。這個資料庫層同時要支援開發者手動優化查詢。
  3. 架構依賴少,容易安裝和部署,社群支援強大。

對适用于生産環境的要求是:

  1. 架構本身輕巧,速度快。
  2. 支援大并發,有成熟叢集部署方案。
  3. 能切換各種類型的資料庫,有memcached等緩存中間件的接口。

在這期間,我試用了Django、drupal、discuz、codeigniter、expressjs、sails,以下記錄選擇過程中對前後端的考慮,希望對有類似需求的朋友有幫助。我的定論留在文章最後。

Django

比較慚愧,出于個人對python不如php和nodejs熟,放棄了Django。Django自帶功能非常強大的ORM,有REST中間件。也有成熟的部署方案。并且有非常好的學習指南(搜"Django book"),推薦各位一定要自己用用。

drupal

要完整地描述和概括drupal有一點難。它既不是某一種具體業務邏輯的架構(把它說成CMS太狹隘),也不是純粹的隻有功能層面(如ORM、視圖渲染)的架構。隻能這樣說:

首先,它包含了強大的功能層,如資料庫層、視圖層。其次,它通過“hook”機制以及一整套完善的附屬功能,讓開發者能非常自由地打造自己的業務邏輯。或者說使得它可以幾乎可以包容任何業務邏輯。類似于AOP,類似于裝飾類。

這裡為不懂的讀者再介紹一下它的核心“hook”。實作一個hook有兩步:一、在業務流程中的某些點聲明可以被hook。二、聲明一個操作,和它要hook到哪個流程中的點。比如:一個使用者注冊子產品聲明,在擷取使用者的注冊資料後,寫到資料庫之前,這一點可以被hook。一個加密子產品聲明,要對注冊資料中的密碼進行加密,并聲明這個操作hook到剛才提到的使用者子產品聲明的點上。這樣在寫到資料庫前,密碼就被加密了。

hook機制早已有之,drupal的精髓在于将其發展并運用到幫助開發者實作業務邏輯上。因為幾乎任何人類的業務邏輯都是流程化的,并且很可能變化。hook滿足了在不直接改變原有邏輯的情況下進行擴充和修改的需求。也是這種機制激活了drupal的開發社群,讓它的每一個子產品都能很好地與其他子產品協作。

一開始我其實是選用了drupal做後端的。第一,它的後端功能十分完善,從使用者注冊到内容管理應有盡有。實在沒有還可以去它強大的社群找支援。第二,drupal在部署、性能優化方面已經非常成熟。但最後仍然棄用了,這裡先說說用的過程。

首先裝上Service子產品自動實作drupal的REST api,這時候節點、使用者什麼的增删改查接口就已經都有了,隻需要按照Services的說明去打開這些接口就行。如果需要跨域調用,還要裝上CORS子產品來允許跨域請求,相關知識請自行google CORS。如果定制更多的查詢接口,可以裝上Services Views子產品,它能直接将views生成的清單變成資料輸出,不過這個子產品和views本身一樣有個讓人很不爽的點:對于多值的字段,不能按數組輸出,隻能輸出成一行字元串,以",“或者其他指定符号分割。

至此,後端已基本可用。然而随着繼續開發,有了更多的需求,比如說投票。drupal有很多投票子產品,但幾乎都是直接嵌入到頁面中的,對api支援不好。于是想自己寫一個。這樣的功能多了以後發現,去找有沒有類似子產品,并調研是否符合我需求和踩坑的時間都已經超過了自己開發的時間。如此一來都已經脫離我”快速開發“的本意了。況且如果都要自己寫,為什麼不選個輕量級的架構呢?比如codeigniter。于是開始便繼續寫,邊尋覓其他架構。drupal整體而言其實應該是非常适合團隊開發的,因為它迫使大家都遵循同樣的機制,這樣就解決了團隊開發容易品質不可控這樣一個最大的風險。對個人開發者來說,總是先搭個架子,或者先看看别人的架子是怎麼搭的,再開始寫代碼,實在有點累。

discuz

看完drupal再看discuz、phpwind什麼的,完全不能接受,架構太亂,控制器、模型、視圖耦合嚴重。沒有對REST api好的支援。略過。

codeigniter

codeigniter确實是非常輕,一個自動映射到控制器的路由系統,一個以AR方式實作的資料庫層,一個可用可棄的視圖層。就這麼簡單。codeigniter同樣也有良好的社群支援,然而更重要的是,正是因為它非常簡單,讓它可以幾乎無痛的與任何第三方類庫內建。不過自己的業務邏輯,就得一點一點自己搭建了。如果是這樣的話,那還是用nodejs的架構更好。

expressjs

expressjs是我所知的目前最成熟的nodejs架構了(如有其它更好的,隻能說我孤陋寡聞了)。選nodejs架構最大優點:第一,沒有切換語言程式設計的痛苦。第二,nodejs在處理并發、大規模部署都已經有成功經驗。第三,能夠良好地支援各種資料庫。如果不是發現了sails的話,我應該就選expressjs了。

sails

對我個人來說,對小架構其實是有戒心的。特别是對于要上線的程式,萬一碰到問題問作者,作者撒手不管,也沒有大規模部署的例子等等情況,上線後可能就會面臨推倒重來的結局。是以不能隻看開發體驗,架構的出身也是很重要的。首先值得說明的是,sails是基于expressjs的。 稍微讓人放心了點。其次,sails雖然自稱實時架構,強調自己對于web socket的封裝。但我目前隻需要用到它的ORM和自動生成REST api。畢竟這兩個基礎的東西看看源碼是可以自己掌握的。下面來介紹以下實踐過程:

首先是路由系統。分為兩個部分,一個是sails對每一個model自動生成的REST api。sails支援标準的REST請求,同時也能讓開發者通過配置檔案将接口修改為常用的REST變種接口。另一部分是使用者自己定義的接口,和codeigniter一樣,sails也是自動将接口映射到相應controller的方法上,非常簡單。它同時還提供了稱之為polices的機制來保護接口,即在調用指定接口之前先用定義好的police檢測一遍,不通過則直接拒絕請求。這樣能讓controller更專注于具體業務邏輯。

然後講講資料層。sails的資料庫擴充卡非常多,幾乎所有的主流的資料庫都能支援。它也是通過寫配置檔案,來實作對字段的定義。一個叫做'Event'的模型例子:

module.exports = {

  attributes: {

        uid: {

            type : 'string',

            defaultsTo : ''

        },

        title: {

            type:'string',

            required : true,

        content : 'string',

        vote : {

            type : 'integer',

            defaultsTo : 0

        }

    },

    afterCreate:function( event,next ){

        Heat.create({

            entity_id : event.id,

            type : 'event'

        }).done(function(){

            next()

        })

    }

};

在這段代碼裡可以看出,sails的定義實作了主流資料庫中對字段的檢測、設定預設值等功能。值得一提的是例子最後的'afterCreate'函數,這是sails提供的類似于drupal hook的東西,它自己稱為"生命周期回調"。它可以讓開發者在模型的增删改查前後自動執行相應的管理操作。在我們的例子裡就是在一條屬于Event模型的記錄建立之後,再建立一條相關的Heat模型的資料。

要注意的是sails ORM中是沒有自動支援模型建的關聯的。以我個人的觀點來說,正好也不需要,因為模型間的關聯常常是造成資料庫查詢負載的罪魁禍首,我常常通過儲存備援資料等方法來盡量減少關聯查詢。是以不支援模型關聯,正好讓我每一個需要關聯的地方都手寫,保障性能。

資料庫我用了mongodb,我的需求裡面有些非結構化的資料可以無需任何轉換地存到資料庫了。sails同時還支援對不同的model使用不同的資料庫,這樣未來要對資料庫調優的話空間就很大了。

最後看看下對CORS的支援,直接在整站的配置檔案中打開crsf選項即可。對于跨站請求,先請求crsf token,之後的請求帶上這個頭就可以跨站了。

結論自然是選擇了sails。目前sails整體項目官方維護得非常積極,功能完備。實在有問題了看看源碼也能夠明白。相對于我的需求來說已經非常好了。

最後整體總結一下:

1.整個開發中前後端完全分離。後端隻提供資料接口,并且支援跨域請求,也便于之後進行其他端的開發。

2.前端用avalon、requirejs、page.js三件套開發體驗非常好。用harp來支援less和coffeescript等,自動編譯,無需任何中間操作。

3.後端用sails來處理業務邏輯和提供REST api。各種功能齊全,開發順暢。

文中内容若有偏頗,望不吝賜教。預祝螢幕前的你新年快樂。