該如何設(shè)計(jì)一個購物車系統(tǒng)呢?
隨著電子商務(wù)的崛起,網(wǎng)站的購物車系統(tǒng)已經(jīng)成了普遍的功能,近幾年Rails快速竄紅,但是很多開發(fā)者對于設(shè)計(jì)真正可用的購物車系統(tǒng)仍然感到有很多的困難。
購物車系統(tǒng)第一步
如何設(shè)計(jì)一個購物車系統(tǒng)呢?在考慮這個問題之前不妨先想想從挑選產(chǎn)品放入購物車,到最后結(jié)賬的環(huán)節(jié)中,可以切出哪些功能點(diǎn)方便規(guī)劃數(shù)據(jù)庫,我們隨機(jī)可以察覺到有商品和購物車,而以下就是一個初步的案例分析圖:

保留金額信息
研判一個購物車可以有多個產(chǎn)品,且一個產(chǎn)品放進(jìn)多個購物車,而這樣的設(shè)計(jì)圖乍看完美卻有個問題:試想當(dāng)一個訪客挑選了一樣產(chǎn)品,并且放入購物車,這時如果該產(chǎn)品的價錢被后臺管理員異動,那么在訪客結(jié)賬之前購物車中的商品價錢也需要異動嗎?
如果這是被允許的,那么筆者可以在訪客結(jié)賬的前一秒,將價錢偷改為幾百萬(之后幾年就是官司人生),于是我們需要新的設(shè)計(jì)避免這樣的悲劇:

這樣就好了許多,還需額外儲存數(shù)量與金額信息,這個金額必須是該產(chǎn)品加入購物車當(dāng)下的數(shù)字,如此結(jié)賬時才不會有價錢異動的爭議。
「訂單」與「購物車」的差異?
「訂單」與「購物車」在電子商務(wù)系統(tǒng)中扮演的角色相似卻也有些差異,相似是兩者都同樣會記錄訪客加入購物車的商品的當(dāng)前價錢與數(shù)量,差異則是「購物車」可編輯商品信息(例如數(shù)量或新增商品),但「訂單」除了付款之外,不可再異動商品信息,且「訂單」會擁有更多如地址、姓名、統(tǒng)編等一類的通信數(shù)據(jù)。
加上「訂單」后的設(shè)計(jì)圖:

付款紀(jì)錄
「訂單」不能變動,唯一可以執(zhí)行的動作就是「付款」。可是付款未必每次都會成功,且無論成功與否都應(yīng)該留個記錄,記錄除了方便管理員查詢、找出問題之外,也能提供給使用者供參考與留存。
「交易紀(jì)錄」不需通信數(shù)據(jù),只需要記錄成功與否、交易編號即可,以下是新的設(shè)計(jì)圖:

你大概會注意到Trade有個params:text屬性,這是用來存放第三方支付服務(wù)所回傳的數(shù)據(jù),可能是JSON或是XML格式,搭配ActiveRecord中的#serialize,可以輕易存取這個字段以供日后的查閱由服務(wù)所回傳的原始交易數(shù)據(jù)。
其他人怎么做?
像購物車系統(tǒng)如此常見的功能,想必也有許多開源項(xiàng)目可以使用,其中最有名的三個項(xiàng)目分別是:
ror_ecommerce
Spree
Piggybak
ror_ecommerce
ror-ecommerce

是由David Henner在設(shè)計(jì)一個電子商務(wù)系統(tǒng)時,順手開源的項(xiàng)目,并且寫了一個網(wǎng)站解釋他的設(shè)計(jì)哲學(xué),即便這個項(xiàng)目是三個之中更新最慢的,但對于想學(xué)習(xí)如何徒手制作電子商務(wù)網(wǎng)站的人來說,筆者認(rèn)為這是最值得參考的學(xué)習(xí)文件。
ror_ecommerce的數(shù)據(jù)庫設(shè)計(jì)與本篇文章的示意圖、以及「Agile Web Development with Rails 4」書中的示例相同。
Spree

Spree是Rails開源電子商務(wù)圈最著名的項(xiàng)目,是由許多資深工程師組成的核心團(tuán)隊(duì)所開發(fā),其中不乏Apache軟件協(xié)會的成員與公司的CTO、CEO,質(zhì)量保證,加上完整的API、文件以及活絡(luò)的社群,此外與ActiveMerchant兼容性高,對于單純只是想要快速架設(shè)電子商務(wù)網(wǎng)站的人來說,Spree堪稱首選。
數(shù)據(jù)結(jié)構(gòu)方面,有別于前述的構(gòu)架,Spree并沒有Cart entity,而是在Order之中新增一個state屬性將「訂單」偽裝成「購物車」的行為,導(dǎo)致Order的行為邏輯比Cart復(fù)雜,但是可以實(shí)現(xiàn)的功能更多。
Piggybak

算是Rails開源電子商務(wù)的后起之秀,沒有Spree那樣完備,但其模塊化的彈性使得許多開發(fā)者也開始投向Piggybak的懷抱,這層關(guān)系有點(diǎn)像Paperclip與CarrierWave之間一樣,后者也是以模塊化著稱。
你可以在Rails中任何一個ActiveRecord model中掛上acts_as_sellable使其成為可以買賣的商品,相當(dāng)簡便,如果你想要的是在既有的網(wǎng)站上加上購物功能,而非重新打造一個購物網(wǎng)站,也許這是讓你選擇Piggybak而非Spree的誘因。
結(jié)語
購物車系統(tǒng)沒有什么「必須」的設(shè)計(jì),不管是什么構(gòu)架的購物車,可以結(jié)賬的就是好的購物車。如果對于設(shè)計(jì)購物車仍有疑惑,也建議不妨多看看他人的開源項(xiàng)目怎么設(shè)計(jì),其實(shí)許多書中找不到的答案,都放在代碼里面,而優(yōu)良的代碼更是可以閱讀的。
