Java生鮮電商平臺-App系統架構開發與設計

Java生鮮電商平臺-App系統架構開發與設計算法

 

說明:閱讀此文,你能夠學習到如下的技術分享數據庫

 

1.Java生鮮電商平臺-App架構設計經驗談:接口的設計
2.Java生鮮電商平臺-App架構設計經驗談:技術選型
3.Java生鮮電商平臺-App架構設計經驗談:數據層的設計
4.Java生鮮電商平臺-App架構設計經驗談:業務層的設計
5.Java生鮮電商平臺-App架構設計經驗談:展現層的設計api

 

接口設計:數組

 

App與服務器的通訊接口如何設計得好,須要考慮的地方挺多的,在此根據個人一些經驗作一些總結分享,旨在拋磚引玉。緩存

安全機制的設計
如今,大部分App的接口都採用RESTful架構,RESTFul最重要的一個設計原則就是,客戶端與服務器的交互在請求之間是無狀態的,也就是說,當涉及到用戶狀態時,每次請求都要帶上身份驗證信息。實現上,大部分都採用token的認證方式,通常流程是:安全

用戶用密碼登陸成功後,服務器返回token給客戶端;
客戶端將token保存在本地,發起後續的相關請求時,將token發回給服務器;
服務器檢查token的有效性,有效則返回數據,若無效,分兩種狀況:
token錯誤,這時須要用戶從新登陸,獲取正確的token
token過時,這時客戶端須要再發起一次認證請求,獲取新的token
然而,此種驗證方式存在一個安全性問題:當登陸接口被劫持時,黑客就獲取到了用戶密碼和token,後續則能夠對該用戶作任何事情了。用戶只有修改密碼才能奪回控制權。服務器

如何優化呢?第一種解決方案是採用HTTPS。HTTPS在HTTP的基礎上添加了SSL安全協議,自動對數據進行了壓縮加密,在必定程序能夠防止監聽、防止劫持、防止重發,安全性能夠提升不少。不過,SSL也不是絕對安全的,也存在被劫持的可能。另外,服務器對HTTPS的配置相對有點複雜,還須要到CA申請證書,並且通常仍是收費的。並且,HTTPS效率也比較低。通常,只有安全要求比較高的系統纔會採用HTTPS,好比銀行。而大部分對安全要求沒那麼高的App仍是採用HTTP的方式。微信

咱們目前的作法是給每一個接口都添加簽名。給客戶端分配一個密鑰,每次請求接口時,將密鑰和全部參數組合成源串,根據簽名算法生成簽名值,發送請求時將簽名一塊兒發送給服務器驗證。相似的實現可參考OAuth1.0的簽名算法。這樣,黑客不知道密鑰,不知道簽名算法,就算攔截到登陸接口,後續請求也沒法成功操做。不過,由於簽名算法比較麻煩,並且容易出錯,只適合對內的接口。若是大家的接口屬於開放的API,則不太適合這種簽名認證的方式了,建議仍是使用OAuth2.0的認證機制。網絡

咱們也給每一個端分配一個appKey,好比Android、iOS、微信三端,每一個端分別分配一個appKey和一個密鑰。沒有傳appKey的請求將報錯,傳錯了appKey的請求也將報錯。這樣,安全性方面又加多了一層防護,同時也方便對不一樣端作一些不一樣的處理策略。數據結構

另外,如今愈來愈多App取消了密碼登陸,而採用手機號+短信驗證碼的登陸方式,我在當前的項目中也採用了這種登陸方式。這種登陸方式有幾種好處:

不須要註冊,不須要修改密碼,也不須要由於忘記密碼而重置密碼的操做了;
用戶再也不須要記住密碼了,也不怕密碼泄露的問題了;
相對於密碼登陸其安全性明顯提升了。
接口數據的設計
接口的數據通常都採用JSON格式進行傳輸,不過,須要注意的是,JSON的值只有六種數據類型:

Number:整數或浮點數
String:字符串
Boolean:true 或 false
Array:數組包含在方括號[]中
Object:對象包含在大括號{}中
Null:空類型
因此,傳輸的數據類型不能超過這六種數據類型。之前,咱們曾經試過傳輸Date類型,它會轉爲相似於」2016年1月7日 09時17分42秒 GMT+08:00」這樣的字符串,這在轉換時會產生問題,不一樣的解析庫解析方式可能不一樣,有的可能會轉亂,有的可能直接異常了。要避免出錯,必須作特殊處理,本身手動去作解析。爲了根除這種問題,最好的解決方案是用毫秒數表示日期。

另外,之前的項目中還出現過字符串的」true」和」false」,或者字符串的數字,甚至還出現過字符串的」null」,致使解析錯誤,尤爲是」null」,致使App奔潰,後來查了很久才查出來是該問題致使的。這都是由於服務端對數據沒處理好,致使有些數據轉爲了字符串。因此,在客戶端,也不能徹底信任服務端傳回的數據都是對的,須要對全部異常狀況都作相應處理。

服務器返回的數據結構,通常爲:

{
code:0,
message: "success",
data: { key1: value1, key2: value2, ... }
}
code: 返回碼,0表示成功,非0表示各類不一樣的錯誤
message: 描述信息,成功時爲」success」,錯誤時則是錯誤信息
data: 成功時返回的數據,類型爲對象或數組
不一樣錯誤須要定義不一樣的返回碼,屬於客戶端的錯誤和服務端的錯誤也要區分,好比1XX表示客戶端的錯誤,2XX表示服務端的錯誤。這裏舉幾個例子:

0:成功
100:請求錯誤
101:缺乏appKey
102:缺乏簽名
103:缺乏參數
200:服務器出錯
201:服務不可用
202:服務器正在重啓
錯誤信息通常有兩種用途:一是客戶端開發人員調試時看具體是什麼錯誤;二是做爲App錯誤提示直接展現給用戶看。主要仍是做爲App錯誤提示,直接展現給用戶看的。因此,大部分都是簡短的提示信息。

data字段只在請求成功時纔會有數據返回的。數據類型限定爲對象或數組,當請求須要的數據爲單個對象時則傳回對象,當請求須要的數據是列表時,則爲某個對象的數組。這裏須要注意的就是,不要將data傳入字符串或數字,即便請求須要的數據只有一個,好比token,那返回的data應該爲:

// 正確
data: { token: 123456 }

// 錯誤
data: 123456
接口版本的設計
接口不可能一成不變,在不停迭代中,總會發生變化。接口的變化通常會有幾種:

數據的變化,好比增長了舊版本不支持的數據類型
參數的變化,好比新增了參數
接口的廢棄,再也不使用該接口了
爲了適應這些變化,必須得作接口版本的設計。實現上,通常有兩種作法:

每一個接口有各自的版本,通常爲接口添加個version的參數。
整個接口系統有統一的版本,通常在URL中添加版本號,好比http://api.domain.com/v2。
大部分狀況下會採用第一種方式,當某一個接口有變更時,在這個接口上疊加版本號,併兼容舊版本。App的新版本開發傳參時則將傳入新版本的version。

若是整個接口系統的根基都發生變更的話,好比微博API,從OAuth1.0升級到OAuth2.0,整個API都進行了升級。

有時候,一個接口的變更還會影響到其餘接口,但作的時候不必定能發現。所以,最好還要有一套完善的測試機制保證每次接口變動都能測試到全部相關層面。

 

技術選型:

 

當你作架構設計時,必然會面臨技術選型的抉擇,不一樣的技術方案,架構也可能徹底不一樣。有哪些技術選型須要作決策呢?好比,App是純原生開發,仍是Web App,抑或Hybrid App?iOS開發,語言上是選擇Objective-C仍是Swift?架構模式用MVC,仍是MVP,或者MVVM?下面根據個人一些經驗對某些方面作點總結分享。

原生/H5
關於用原生好,仍是用H5好的爭論從沒間斷過。但我以爲,脫離了實際場景來討論孰好孰壞意義不大。就說咱們目前正在作的項目,先說明下背景:

不止要作Android和iOS App,也要作微信公衆號;
H5人員缺少,只有一兩個兼職的可用,並且不可控因素很高;
咱們對原生比較熟;
開發時間只有半個月。
首先,需求上來講,大部分頁面用H5實現,能夠減小不少工做量。但由於不可控因素過高,而時間又短,風險太大。而咱們對原生比較熟,開發效率比較高,不少東西我也控制得了,風險相對比較低。並且,咱們的主推產品是App,微信屬於輔助性產品,因此,微信要求也沒那麼高。所以,我決定以原生爲主,H5爲輔,App大部分頁面用原生完成,小部分用WebView加載H5。

另外,WebView加載H5也有兩種模式,一種是加載服務器的H5頁面,一種是加載本地的H5頁面。加載服務器的H5頁面比較簡單,WebView只要load一下URL就能夠了。加載本地的H5頁面,則須要將H5文件存放在本地,包括關聯的CSS和JS文件。這種方式相對比較複雜,不過,加載速度會比第一種快不少。咱們當前項目基於上面考慮,只能選擇第一種方案。

若是人員和時間資源充足的話,那又如何選型呢?毫無疑問,我會以H5爲主,微信和App都有的頁面統一用H5,App專有的部分,好比導航欄、標題欄、登陸等,才用原生實現。另外,WebView裏的H5有點擊事件時,也許是URL連接,也許是調用JS的,都不會讓它直接在該WebView裏作跳轉,須要攔截下來作些原生處理後跳轉到一個新的原生頁面,原生頁面也許嵌入另外一個WebView,用來展現新的H5頁面。這是簡單的例子,關於Hybrid App詳細的設計,之後再講。另外,關於H5,絕對是大趨勢,強烈建議全部App開發人員都去學習。

Objective-C/Swift
我在項目中選擇了Swift,主要基於三個緣由:

Swift真的很簡潔,生產效率很高;
Swift取代Objective-C是必然的趨勢;
目前iOS只有我一我的開發,不須要顧慮到團隊裏沒人懂Swift。
若是你的團隊裏沒人懂Swift,那仍是乖乖用Objective-C吧;若是有一兩個懂Swift的,那能夠混合開發,並讓不懂的人儘快學會Swift;若是都懂了,不用想了,直接上Swift吧。

當語言上選擇了Swift,相應的一些第三方庫也面臨着選型。好比,依賴庫管理,Objective-C時代大部分用CocoaPods,Swift時代,我更喜歡Carthage。Carhage是用Swift寫的,和CocoaPods相比,輕耦合,也更靈活。我我的也不太喜歡CocoaPods,使用起來比較麻煩,耦合性也較高,我使用過程當中也常常出問題,並且還老是不知道該怎麼解決,要移除時也是很是麻煩。

再推薦幾個關於Swift的第三方庫:

Alamofire:Swift版本的網絡基礎庫,和AFNetworking是同一個做者
AlamofireImage:基於Alamofire的圖片加載庫
ObjectMapper:Swift版本的Json和Model轉換庫
AlamofireObjectMapper:Alamofire的擴展庫,結合了ObjectMapper,自動將JSON的Response數據轉換爲了Swift對象
MVC/MVP/MVVM
先分別簡單介紹下這三個架構模式吧:

MVC:Model-View-Controller,經典模式,很容易理解,主要缺點有兩個:
View對Model的依賴,會致使View也包含了業務邏輯;
Controller會變得很厚很複雜。
MVP:Model-View-Presenter,MVC的一個演變模式,將Controller換成了Presenter,主要爲了解決上述第一個缺點,將View和Model解耦,不過第二個缺點依然沒有解決。
MVVM:Model-View-ViewModel,是對MVP的一個優化模式,採用了雙向綁定:View的變更,自動反映在ViewModel,反之亦然。
架構模式上,我不會推崇說哪一種模式好,每種模式都各有優勢,也各有極限性。越高級的模式複雜性越高,實現起來也越難。最近火熱的微服務架構,比起MVC,複雜度不知增長了多少倍。

我在實際項目中思考架構時,也不會想着要用哪一種模式,我只思考現階段,以現有的人力資源和時間資源,如何才能更快更好地完成需求,適當考慮下如何爲後期擴展或重構作準備。就說我前段時間分享的Android項目重構之路系列中講的那個架構,確切地說,都不屬於上面三種架構模式之一。

 

數據層的設計

一個App,從根本上來講,就是對數據的處理,包括數據從哪裏來、數據如何組織、數據怎麼展現,從職責上劃分就是:數據管理、數據加工、數據展現。相對應的也就有了三層架構:數據層、業務層、展現層。本文就先講講數據層的設計。

數據層,是三層架構中的最底層,負責數據的管理。它主要的任務就是:

調用網絡API,獲取數據;
將數據緩存到本地;
將數據交付給上一層。
根據這三個任務,數據層能夠再拆分爲三層:網絡層、本地數據層、交付層。

網絡層
網絡層主要就是對網絡API的封裝。關於API的設計,該系列的第一篇文章接口的設計已經講過一些。關於如何封裝,能夠參考Android項目重構之路系列的架構篇和實現篇,其中接口層和本文的網絡層是同樣的。

還有一些在前面的文章中沒有說起到的,在此作一些補充。

首先是不一樣網絡狀態的處理。當網絡不可用時,則不該該再去調用API;當網絡可用,但不是WIFI時,有些比較耗流量的操做也應該禁止,好比上傳和下載大文件;當網絡狀態不一樣時,還能夠採用不一樣的網絡策略,好比,當網絡爲WIFI時,當前API能夠返回更多更全面的數據,還能夠預先加載相關聯的其餘API。

其次,爲了節省流量,接口的設計上能夠對數據進行簡化。例如,對於一些列表類的接口,能夠這麼設計:只返回更新的部分,好比,上一次請求返回了10條按時間排序的數據,第一條數據爲最新的,id爲101,當發起下一次請求時,將101的id做爲參數調用API,API查到該id,發現該id以後又新增了兩條數據,API則只返回新增的這兩條數據。

另外,爲了保證程序的健壯性,調用API時,對入參的合法性檢查也是頗有必要的。並且,也應該定義好本地的錯誤碼和錯誤信息,保證每一個錯誤都能正常解析。

本地數據層
本地數據層主要就是作緩存處理,這須要設計好一套緩存策略。設計緩存策略時,有幾個問題須要考慮清楚:

哪些須要緩存?哪些不須要緩存?
緩存在哪裏?數據庫?文件?仍是內存?
緩存時間多長?
哪些須要緩存?

將全部數據都緩存是不明智的,不一樣的數據應該有不一樣的緩存策略,好比一個電商App,首頁的商品列表數據應該緩存,並且緩存時間應該比較長,而每一個商品的詳情數據就不必緩存或緩存時間很短。對於一份數據需不須要緩存,判斷標準能夠是:用戶查看該數據的頻率高不高?首頁商品列表是用戶每次啓動都會看到的,而每一個商品的詳情用戶最多隻看幾回。

緩存在哪裏?

從內存讀取數據是最快的,但內存很是有限。所以,內存通常只用來緩存使用頻率很是高的數據。

文件緩存主要就是圖片、音頻、視頻了。

數據庫能夠保存大量數據,主要就是用於保存商品列表、聊天記錄之類的關係型數據。

然而,無論緩存在哪裏,都須要限定好緩存的容量,要按期清理,否則會越積越多。

緩存時間多長?

首先,每份緩存數據都應該設置一個緩存的有效時間,有效期的起始時間以最後一次被調用的時間爲準,當該數據長時間沒有再被調用到時,就應該從緩存中清理掉。

緩存的有效時間應該設多長呢?能夠短至一分鐘,長至一星期甚至一個月,具體因數據而異。通常內存的緩存時間不宜太長,程序退出基本就要所有清理了。文件緩存能夠設置保留一天或一個星期,能夠每隔一天清理一次。數據庫緩存再久一些也無所謂,但最好仍是不要超過一個月。

交付層
交付層其實就是一個向上層開放的交互接口層,是上層向數據層獲取數據的入口。上層向數據層請求數據,它是不關心數據層的數據是從緩存獲取仍是從網絡獲取的,它只關心結果,數據層能給到它想要的數據結果就OK了。所以,交付層主要就是定義一堆開放的接口或協議。

若是接口或協議很是多,那麼,將接口或協議按照模塊劃分也是有必要的。好比微信,按模塊劃分有:IM、公衆號、朋友圈、錢包、購物、遊戲等等。模塊之間應該儘可能相對獨立、鬆耦合。

 

業務層的設計

業務層其實並不複雜,可是大部分開發人員對其職責並無理解清楚,從而使其淪落爲一個數據中轉站。我以前分享過的Android項目重構之路系列中提到的核心層,其實就是這裏所講的業務層。但有很多讀者反映,他們在實際項目中就只是作一下參數檢查,而後直接調用API,與展現層對接的接口基本也與API的接口一致的。這樣,業務層無疑就已經變爲了一個數據中轉站。

業務層的職責
因此,設計業務層以前,對業務層的職責要先真正理解清楚。這裏,我舉兩個栗子說明一下。

第一個是新用戶註冊的例子。註冊時,界面上通常都會要求用戶輸入手機號、驗證碼、密碼和確認密碼。可是,API接口通常只會有三個參數:手機號、驗證碼和密碼,不會有確認密碼。所以,調用接口以前,密碼和確認密碼的一致性檢查是必須的。同時,也要檢查這些數據是否爲空、手機號是否符合規範、驗證碼是否有效、密碼有沒有包含了特殊字符等。正確姿式就是當全部檢查都經過了以後,才調用API接口。最後,調用註冊接口成功後,可能還要再調用一次登陸接口,並可能將用戶登陸信息緩存起來,方便用戶下次啓動應用時自動登陸。全部這些都屬於業務邏輯處理,也就是業務層的工做。

第二個是涉及用戶驗證的例子。好比,在一個電商App,當用戶瀏覽某個商品,點擊購買時,App首先會判斷用戶是否已經登陸,如未登陸,則會跳轉到登陸頁面讓用戶先登陸。若是已經登陸,但token已通過期,那須要先去獲取新的token,以後才能進行下一步的購物操做。這些邏輯處理,也是業務層的工做。

所以,簡單點說,業務層就是處理業務邏輯,包括數據的檢查、業務分支的處理等。好比上面第二個例子,可能不少人就會將用戶是否已經登陸的判斷直接在界面上作處理,當確認登陸後,token也是有效的以後,才調用業務層作購買商品的操做,這就是致使業務層淪落爲API的數據中轉站的直接表現。

業務層的交互
只有真正理解了業務層的職責以後,纔能有效地設計業務層與外層的交互接口。

業務層向下,與數據層交互;向上,與展現層交互。

與數據層交互只是調用數據層的接口獲取數據,而與展現層交互則須要提供接口給展現層調用。由於業務處理通常屬於比較耗時的操做,主要在於底層的網絡請求比較耗時,因此提供給展現層的接口數據結果應該以異步的方式提供,所以,接口上就須要提供個回調參數,返回業務處理以後的結果。我以前分享過的Android項目重構之路:實現篇有講到一種實現方式,可參考。

 

展現層的設計
三層架構中,數據層和業務層都已經作過了簡單的分享,最後,就剩下展現層了。本篇就給各位分享下我在展現層設計方面的一些經驗心得。

展現層是三層架構中最複雜的一層了,須要考慮的包括但不限於界面佈局、屏幕適配、文字大小、顏色、圖片資源、提示信息、動畫等等。展現層也是變化最頻繁的一個層面,天天改得最多的就是界面了。所以,展現層也是最容易變得混亂不堪的一個層面。一個良好的展現層,應該有較好的可讀性、健壯性、維護性、擴展性。

三原則
我在Android項目重構之路:界面篇中提到過三個原則,要設計好展現層,至少須要遵循好這三條基本的原則:

保持規範性:定義好開發規範,包括書寫規範、命名規範、註釋規範等,並按照規範嚴格執行;
保持單一性:佈局就只作佈局,內容就只作內容,各自分離好,每一個方法、每一個類,也只作一件事情;
保持簡潔性:保持代碼和結構的簡潔,每一個方法,每一個類,每一個包,每一個文件,都不要塞太多代碼或資源,感受多了就應該拆分。
關於這三個原則詳細的解說,界面篇已經講過的,我這裏就再也不重複。在此,我只作些補充。

關於規範,Android方面,我已經分享過一套Android技術積累:開發規範,主要分爲書寫規範、命名規範、註釋規範三部分。iOS方面,蘋果已經有一套Coding Guidelines,主要屬於命名方面的規範。當咱們制定本身的開發規範時,首先就要遵照蘋果的這份規範,在此基礎上再加上本身的規範。

最重要的不是開發規範的制定,而是開發規範的執行。若是沒有按照開發規範去執行,那開發規範就等於形同虛設,那代碼混亂的問題依然得不到解決。

另外,Android系統自己已經對資源進行了很好的分離,字符串、顏色值、尺寸大小、圖片、動畫等等都用不一樣的xml文件定義。而iOS系統在這方面就遜色不少,只能本身實現,其中一種實現方案就是經過plist文件的方式實現和Android同樣的機制。

工程結構
工程結構其實就是模塊的劃分,無非分爲兩類:按業務劃分或按組件劃分。

好比一個電商App,可能會有首頁、附近、分類、個人四大模塊,工程結構也根據這四大模塊進行劃分,Android可能就分爲了四個模塊包:

com.domain.home 首頁
com.domain.nearby 附近
com.domain.category 分類
com.domain.user 個人
一樣的,iOS則分爲四個分組:home、nearby、category、user。

以後,每一個模塊下相應的頁面就放入相應的模塊。那麼,問題來了,商品詳情頁應該屬於哪一個模塊呢?首頁會跳轉到商品詳情頁,附近也會跳轉到商品詳情頁,分類也會跳轉到商品詳情頁,用戶查看訂單時也能跳轉到商品詳情頁。有些頁面,並不能很明顯的區分出屬於哪一個模塊的。我接手過的,按業務劃分的二手項目中(即不是由我搭建的項目),我要找一個頁面時,我認爲應該屬於A模塊的,但在A模塊卻找不到,問了同事才知道在B模塊。相似的狀況出現過不少次,並且不止出如今我身上,對業務不熟悉的開發人員都會出現這個問題。並且,對業務不熟悉的開發人員開發新的頁面或功能時,若是對業務理解不深,劃分出錯,那也將成爲問題,其餘人員要找該頁面時更難找到了。

所以,我更喜歡按組件劃分的工程結構,由於組件每一個人都懂,無論對業務熟不熟悉,查找起來都明顯方便不少。Android按組件劃分大體以下:

com.domain.activities 存放全部的Activity
com.domain.fragments 存放全部的Fragment
com.domain.adapters 存放全部的Adapter
com.domain.services 存放全部的Service
com.domain.views 存放全部的自定義View
com.domain.utils 存放全部的工具類
iOS的分組則大體以下:

controllers 存放全部ViewController
cells 存放全部Cell,包括TableViewCell和CollectionViewCell
views 存放全部自定義控件或對系統控件的擴展
utils 存放全部的工具類
基類的定義
Android的Activity、Fragment、Adapter,iOS的ViewController,分別定義一個基類,將大部分通用的變量和方法定義和封裝好,將減小不少工做量,並且有了統一的設置,也會減小代碼的混亂。好比我在Android項目重構之路:實現篇中提到的KBaseActivity和KBaseAdapter的實現就是例子,固然還能夠抽離出更多變量和方法。

每一個Activity的onCreate()方法,通常分爲三步:

變量的初始化;
View的初始化;
加載數據。
所以,其實能夠將onCreate()方法拆分紅三個方法:

initVariables()
initViews()
loadData()
在基類中將這三個方法定義爲抽象方法,由子類去實現,這樣,子類就不須要實現onCreate()方法了,只要實現更細化的上述三個方法便可。

iOS的ViewController也是一樣的方式,這裏就不重複了。

相關文章
相關標籤/搜索