2017-07-20

MVC, ASP.NET, and Salesforce VisualPage

表面上寫了好一陣 web application,骨子裡更多是寫 backend, libraries 與一些整合性的 service,UI 這塊一直非常薄弱。一開始想學 Struts2,看了些書,但一直沒落實到開發上;後來因為 Activiti BPM 接觸到 Vaadin 6,大概知道 component-based MVC 的樣貌,之後又碰了些 ASP.NET WebForm 這種以 view 為主的架構,算是有些粗略印象,該是要好好釐清的時候。

傳統的 MVC 源自單機的圖形化程式,model, view, controller 都在同一個 runtime 上,彼此溝通無礙;model 變化時可以順利更新 view,controller 只要負責處理 request 與 response 就好,簡單來說就是源自 UI control 與 model 的 events。其中 model 更新 view 這段連結又稱為 data binding。

這件事到了 web application 世界中有了大挑戰。

在 web application 中,view 屬於 client code,由 rendering engine 負責生成,而 controller 與 model 為 server code,server 端無法隨時連接 client 作畫面更新;因此出現了 MVC Model2。MVC Model2 並沒有解決問題,只是因應限制,釐清與傳統 MVC 的不同,MVC Model2 中,model 不負責將結果更新到 view,改由 controller 負責,至於如何實現一時還沒有答案。

這個時期出現幾種作法,最直覺得就是在 view 上頭掛一段 JavaScript,定期刷新頁面,包括畫面跟資料。微軟為了比較好的使用者體驗,在 1999 率先提出 XMLHttpRequest (XHR) 的概念,也就是說 client 刷新不再以整個 view 為單位,可以只針對需要的資料取得相關的 XML 即可。整個標準化的過程一直到 2002/6 Mozilla Gecko 發佈 1.0 版、2004/2 Safari 1.2 發佈之後才真正落實。

2004-2005 這兩年迎來巨變。

Gmail 跟 Google Maps 相繼於 2004/4 與 2005/2 發表,大家也發現 XMLHttpRequest 可以搭配 client-side JavaScript asynchronous 的行為,讓 JavaScript 負責處理 XMLHttpResponse 就好,不但可以刷新部份頁面,也可以部在一開始就載入完整頁面,當使用者觸發後再進一步抓資料,且不影響既有畫面瀏覽。AJAX 正式登場,並作為 2004/11 Firefox 1.0 發表時的主打特點。

某程度上來說 AJAX 算是解決了部份 data binding 的問題,但主動權仍落在 client 端,並不是真的由 controller 更新 view,屬於單向 (one-way) data binding。即便如此,AJAX 也讓更多人把目光從 data binding 轉移到另一個重點,model 的複用,衍生 component-based MVC frameworks。

對於初入前端的人來說,最棘手的就是各瀏覽器相容性問題,一種解法是使用 cross-browser JavaScript UI libraries,另一種就是交由 MVC frameworks 產出 cross-browser UI components。Component-based MVC 就是後者的產物。

這邊的 component 指的是 UI component;例如一個下拉選單,或 textbox;通常會帶一些資料跟檢核機制。相對於 MVC,component 比較像是 model 在 view 中的呈現,卻又帶一點 controller 對資料檢核的邏輯,但其呈現的不是一整的 page 而是各別 model。使用 component-based MVC 有個很大的好處,前面說的 one-way data binding 都在 framework 中處理好了,不需要額外程式碼,開發人員只要專注如何組裝、複用這些 UI components 即可。

但也因為 components 大大削弱了原本 MVC Model2 中 controller 的角色,處理 request 與更新 view;因此這類 component-based MVC framework 標榜的 architectural pattern 往往不是 MVC Model2,而是 MVP 或 MVVM 等。

ASP.NET 中的 code behind 算不算是 controller?
先回頭看看 controller 的 3 項主要職責:(1) 處理 request;(2) 根據執行結果選擇不同的 view;(3) 更新 view 中 model 的值。從功能性來看,code behind 完全能達到上面三者的要求,那究竟怪在哪裡?

首先 code behind class 裡頭的 member fields 多半宣告在 ASPX/ASPC 中,而 ASPX/ASPC 又只能綁定一個 code behind class,導致兩者 1:1 的強耦合關係,微軟更因此給了個很妙的名稱,page controller;這樣的模糊命名讓原本寫 ASP.NET Web Forms 的開發人員,瞬間也可以宣稱自己在寫 MVC;只是沒有以 ASP.NET MVC 為基礎,真正的 ASP.NET MVC 用的是 front controller pattern。

除了耦合性議題,另一個 page controller 讓人覺得詭異的地方是:在針對執行結果導向對應的 view 時,page controller 往往使用 HttpRequest.Redirect,依賴瀏覽器處理 HTTP redirect 行為,而不是走 server-side internal routing。

所以這個問題若使用微軟模糊的回答,答案是 YES;但面對多數的 MVC framework 是完全對應不起來。

Salesforce VisualPage 的 controller
相對於 ASP.NET ASPX/ASPC 與 code behind 的結構,VisualPage 也有個類似 code behind 的 class,稱為 controller。比 code behind 稍微好一點,VisualPage 所有欄位的宣告被放在 controller,換言之,controller 可脫離 VisualPage 獨立測試。此外在預載欄位資料上,ASP.NET 透過 code behind 塞入資料,而 VisualPage 的 controller 只提供 getter,把塞資料的主動權交給 view 本身,跟 AJAX data binding 的作法一致。

至於連動選單的更新,VisualPage 的作法就完全按照 AJAX data binding,不需要重載整個頁面,由瀏覽器送出 XMLHttpRequest 後刷新 UI component 完成。

See also:
[1] XMLHttpRequest (wikipedia; Mozilla.org)
[2] AJAX Programming (wikipedia)
[3] Ajax: A New Approach to Web Applications
[4] GUI Architectures (Martin Fowler)
[5] MVC是一個巨大誤會
[6] MVVM (wikipedia)
[7] MVP (wikipedia)
[8] Webforms vs MVC and Why MVC is better?
[9] MVC-structured Visualforce Example