復雜表單應用解耦,淘寶機票訂單實踐

0 評論 4957 瀏覽 33 收藏 17 分鐘

  背景

在web應用中,復雜表單這類web應用富交互元素多,業(yè)務邏輯復雜,犬牙交錯,且需求變化頻繁。及容易成為晦澀和幽暗之地,也經(jīng)常是各種代碼壞味道的來源。針對這種典型的復雜應用,本文以淘寶機票訂單為例提出一種架構模式梳理和消化表單帶來的復雜性。

解決復雜表單的的第一步,劃分模塊。

概念上,為了復用和解耦方便,應將模塊按照功能的內(nèi)聚程度進行劃分。強相關,頻繁溝通和交互的功能應該歸為一個模塊。模塊間盡量不存在依賴關系。也就是常說的“高內(nèi)聚,低耦合”。

如下圖所示,淘寶機票訂單頁面主要有被分為7個主要模塊。

  模塊劃分完畢,下一步確認組成模塊的組件。

關于模塊和組件的區(qū)分。一般按照以下三個緯度考量。

1.是否有業(yè)務邏輯參與。

2.是否包含html。

3.是否具備一定獨立性。

“模塊”,定義為一個包含”html”、”css(圖片被認為是css的一部分)“、”javascript”的代碼集。模塊的應用方式多為通過web模板技術(如:velocity、freemarker、php)。因為包含了html,使得模塊必須通過服務端合并加載并且最終推送到用戶瀏覽器。此外,“模塊”還是具備一定獨立業(yè)務和交互的集合,最好可以被其他頁面引用。良好的獨立性也可以幫助協(xié)同開發(fā),在實際開發(fā)中可多人可以并行開發(fā)多個獨立模塊,提高效率。

“組件”,定義為一個僅包含”css”和”javascript”的代碼集。正因為不包含html,所以組件可通過javascript異步加載。因為這種可異步加載的特性,組件在復用方面的容易性遠超模塊。組件沒有業(yè)務邏輯或者僅有少部分公共業(yè)務邏輯。業(yè)務邏輯越多,組件的可復用性就越低。

  模塊、組件間通訊

組件/模塊劃分的目的是將彼此間相對獨立的功能分離,前面通過模塊和組件的劃分解決了分離問題。實際中,模塊之間存在協(xié)作關系。模塊間應以一種輕量的方式協(xié)作。一般的為了更好的分離和解耦,可以考慮用廣播的方式在模塊間溝通,考慮使用事件的方式在組件間通訊。

如下圖所示,淘寶機票訂單頁面的數(shù)據(jù)流向。

  不同模塊在后期均有可能擴展小功能。例如不定期的活動優(yōu)惠等。事件廣播可以讓不同模塊/組件間新增功能影響面縮小。在淘寶機票訂單中應用中,使用廣播組件通訊主要用來完成以下意圖。

  1、知會。

知會的特性在于異步通訊。廣播發(fā)起方只需要放出事件,無需等待其他關注者完成處理。稱為異步廣播。例如表單模塊的內(nèi)容變更需要知會到顯示訂單金額的模塊,顯示訂單金額的模塊接受事件后需要更改金額。

基于這種方式的通訊,各模塊之需要做好自己的事情,外部關注的事件廣播出去即可。異步廣播還有一個好處是系統(tǒng)堅固性比較強,廣播發(fā)送者不會因為事件監(jiān)聽者的使用不當而異常。

2、請求數(shù)據(jù)

例如,模塊6(負責提交)需要在被點擊后從模塊2(乘機人表單),模塊4(聯(lián)系地址)、模塊7(金額計算)。獲取具體數(shù)據(jù)提交。請求數(shù)據(jù)的場景特性在于,廣播發(fā)起者需要等待事件處理者完成處理后再繼續(xù)下一步行為。稱為同步廣播。

基于此機制。提交模塊只需要負責綜合校驗,浮層,網(wǎng)絡請求及異常處理。而具體請求的內(nèi)容由其他模塊決定。對后續(xù)模塊的擴充起到了很好的左右。

復雜組件拆分

模塊和組件劃分完畢后,可能會發(fā)現(xiàn)某些組件非常復雜,幾乎占據(jù)了整個web應用一半以上的代碼。這部分組件由純js實現(xiàn),并且使用javascript模塊加載器加載。

同一個組件大量代碼糾結在一起,最終還是會導致架構腐化。因此,復雜組件需要進一步拆分。在淘寶機票訂單中,乘機人信息組件是一個復雜組件。如下圖所示:

  拆分這類輸入型的復雜組件,一般來說有兩種思路方式。

縱切,組件樹型式。

將組件進一步劃分為更細力度的輸入組件,將每個輸入域作為一個單獨組件。最終形成一個組件樹。

  這樣的組織方式結構嚴謹層級清晰,最大的優(yōu)點是很容易支持字段擴展。

但考慮如下場景,為了盡量友好的提示用戶,需要在輸入域外的某處增加提示幫助。

  這種場景下組件樹的組織方式每次在面對變化時就會略顯手忙腳亂。難道把每個地方出現(xiàn)的tip都座位獨立組件看待嗎?

字段級的適普性降低了適應細節(jié)調整的能力,付出的代價在于界面體驗。

橫切,AOP式。

將所有輸入域抽象的看待為同一個組件。按照組件的富應用特性分層看待。在本例中,乘機人組件被按照從簡單到復雜分為3個切面。

切面1-基礎展現(xiàn)層只負責最基礎的可完成輸入的表單控件,及基礎dom管理。

切面2-富展現(xiàn)層負責修飾base層的基礎html控件,形成富輸入控件。

切面3-校驗層負責對base層的輸入數(shù)據(jù)進行業(yè)務級校驗。

未來,如果新增tip或者其他業(yè)務邏輯,增加一個新切面即可,完全或者很少需要修改老代碼文件。

  淘寶機票訂單采用了AOP這種方式,從最終代碼量上來看,可以看出復雜度被比較均衡的分布到不同文件中去。

  同樣,這種方式也有局限,如果需要擴展字段,那將是一個災難,你有可能需要到每一個切面里面去做修改。

有句老話說的好,沒有最優(yōu)方案,只有最適合的解決方案,任何解決方案,都需要放到具體場景中去評判。事實上,對這個問題的進一步研究,可以發(fā)現(xiàn)以下規(guī)律。

對于一個組件、模塊,同時追求簡單設計、適普性(字段級擴充)、界面體驗是不可能的。如果場景需要適應字段靈活擴展,那就采用縱切的模式。如果使用場景需字段確定,需要更多細節(jié)控制力度,那就橫切,AOP式。如果兩者都要兼顧,就需要引入復雜設計,綜合運用橫切和縱切。但是這樣形成的最終設計會很復雜,開發(fā)和可維護性上會有代價付出。

  對于淘寶機票這類互聯(lián)網(wǎng)應用,使用了橫切的方式來拆分組件,因為在這個場景中,字段的數(shù)量是相對固定的,而圍繞固定數(shù)量字段的優(yōu)化需求是層出不窮的。然而在企業(yè)內(nèi)網(wǎng)應用或者網(wǎng)站后臺web應用中,字段的變化會比較頻繁。建議主要采用縱切的思路劃分。

表單校驗

有表單的地方就有校驗。項目初期,校驗的功能總是不起眼。等待項目后期時候經(jīng)常會發(fā)現(xiàn)校驗已經(jīng)占據(jù)了巨大工作量并且成為海量bug的源頭。因此校驗是一種典型的容易被輕視單又蘊含巨大工作量的事情,需要特別對待,專門設計。

一般來說,這根據(jù)校驗根據(jù)其復雜度可以分為以下兩類:

格式校驗

格式校驗一般是校驗用戶輸入的格式是否滿足要求,比如是否數(shù)字、電話號碼、郵箱等等。此類校驗的特點是校驗域單一,一般只對一個input或者某個組件的value進行檢查。格式類校驗應與與用戶展現(xiàn)非常接近,一種非常好的做法是將此類校驗信息直接描述在html標簽屬性中。html5中input的pattern屬性就是一種基于這種思想的解決方案。

邏輯校驗

邏輯校驗是滿足格式校驗后,繼續(xù)進行的與業(yè)務相關的校驗,例如是否存在相同用戶名,輸入的生日是否和身份證號不符等等。此類校驗的一般涉及多個輸入域,要綜合處用戶的輸入內(nèi)容一起校驗。此類校驗邏輯復雜,不適合寫在html中。

目前有很多流行的form校驗框架解決校驗問題,如何引入合適的校驗框架,先從理解校驗這件事的過程開始。

典型的一個校驗過程如下,用戶在某個input處完成輸入,應用在某個時刻被觸發(fā)校驗,可以是失去焦點或者keyup或者其他。被觸發(fā)的校驗過程找到此處input所需要的校驗規(guī)則(有時候這個規(guī)則被直接寫在html中)判單正確與否,如果正確,可能有提示,如果錯誤,可能也有提示。

從以上場景的描述中,可以找到校驗的幾個關鍵環(huán)節(jié)。這里局部采用一下管理學上經(jīng)典的5w1h問題分析方法來分析問題

1.who:?哪個輸入控件的內(nèi)容需要校驗。這是框架是解決不了的。要對哪個輸入域做校驗應該是應用傳遞進入的。

2.when:?何時被觸發(fā)校驗。比如說是“who”失去焦點時。變化太多,框架解決不了。只能被動觸發(fā)。

3.what:?做什么校驗。有時候這個”what”被寫在html中。基本上,所有格式校驗都是固定的,這個問題應框架解決。但框架應預留接口做更加復雜的業(yè)務校驗。

4.how:?校驗完畢后的動作??蚣懿荒軟Q定做什么,但是在校驗結果出來后,框架應能知會到外部調用者。

在設計框架或者選擇已有框架時,首先要區(qū)分框架的邊界,簡單來說,就是做什么和不做什么??蚣軕獙崿F(xiàn)相對固定的業(yè)務流程。同時對可變部分預留足夠的靈活性。

一個通用的校驗框架一定是不含界面部分的。界面是多變和難以窮舉的,是用tip顯示錯誤,還是在輸入域附近顯示,是否需要動畫,是否需要修改輸入域的視覺狀態(tài),這些可變化的部分應為框架外部內(nèi)容,由更專業(yè)的tip組件或者popup來完成??蚣苤粦撠撠熢谛r炌瓿蓵r候知會相關組件完成顯示錯誤提示等若干事情。

基于以上的分析,校驗框架應該具備以下規(guī)格

1. 解決what問題。內(nèi)置了各種格式校驗規(guī)則,如電話號碼、e-mail等.并且能夠靈活定義新的邏輯校驗。

2. 解決who問題。說明如何根據(jù)輸入的字符真正找到who對應的value。并且能夠對于這個who使用哪些校驗規(guī)則

3. 解決when問題。提供一個觸發(fā)校驗的方法。

4. 解決how問題。產(chǎn)生校驗結果后能夠知會外部的功能框架。

在淘寶機票訂單應用中,依據(jù)上述原則自行設計了一個Validator框架,接口定義如下,Validator是校驗框架對象。

在構造函數(shù)中提供表格化的校驗邏輯定義型式。如下圖所示,傳遞如下結構,定義每個字段對應的校驗方式。在下圖中,定義每行為一個field,每個field有若干rule,每個rule可以是框架內(nèi)置的格式校驗,也可以是自定義的邏輯校驗,實際上是函數(shù)名。

  Validator框架提供validate()方法,validate方法有兩個行為,如果不指定參數(shù),將依次執(zhí)行完所有field的校驗,并且將最終結果返回。如果執(zhí)行一個field name,框架將只校驗field name對應的輸入域。

一旦執(zhí)行validate()方法,無論校驗結果如何,框架均向其觀察者發(fā)送事件’onValidate’。以便觸發(fā)后續(xù)動作。

一些輔助參數(shù),需要提供一個從field name找到輸入域value的function。

  總結

在處理復雜表單時,首先通過合理模塊、組件劃分,將復雜度分散。然后利用詳細和廣播機制解決分散的模塊和組件間通問題。接著,過于復雜的組件要考慮進一步拆分,具體拆分的方式有縱切和橫切兩種,根據(jù)具體使用場景決定。最后,不要小看了校驗,需要特別對待,專門設計。

網(wǎng)友點評:非常認同在做表單應用開發(fā)時先考慮表單校驗的實現(xiàn)方案?,F(xiàn)在校驗多與服務端一起進行,校驗分為格式校驗和邏輯校驗,前端可以只考慮格式校驗,邏輯校驗可以直接交給服務端處理,校驗框架只需要做好反饋處理就好。

更多精彩內(nèi)容,請關注人人都是產(chǎn)品經(jīng)理微信公眾號或下載App
評論
評論請登錄
  1. 目前還沒評論,等你發(fā)揮!