摘要:我發現也許是不是由於目前12306的核心領域模型設計的不夠好,致使用戶購票時要處理的業務邏輯異常複雜,維護數據一致性的難度也幾百倍的上升,同時面對高併發的訂票也難以支持很高的TPS。我以爲,越是複雜的業務,就越要重視業務分析,重視領域模型的抽象和設計。若是不假思索,憑以往經驗行事,則極可能會被以往的設計經驗先入爲主,陷入死衚衕。我發現技術人員每每更注重技術層面的解決方案,好比一上來就分析如何集羣、如何負載均衡、如何排隊、如何分庫分表、如何用鎖,如何用緩存等技術問題,而忽略了最根本的業務層面的思考,如分析業務、領域建模。我認爲越是複雜的業務系統,則越要設計一個健壯的領域模型。若是一個系統的架構咱們設計錯了,還有補救的餘地,由於架構最終沉澱的只是代碼,調整架構便可(一個系統的架構自己就是不斷演進的);而若是領域模型設計錯了,那要補救的代價是很是大的,由於領域模型沉澱的是數據結構及其對應的大量數據,對任何一個大型系統,要改核心領域模型都是成本很是高的緩存
需求簡述
12306這個系統,核心要解決的問題是網上售票。涉及到2個角色使用該系統:用戶、鐵道部。用戶的核心訴求是查詢餘票、購票;鐵道部的核心訴求是售票。購票和售票實際上是一個場景,對用戶來講是購票,對鐵道部來講是售票。所以,咱們要設計一個在線的網站系統,解決用戶的查詢餘票、購票,以及鐵道部的售票這3個核心訴求。看起來,這3個場景都是圍繞火車票展開的。數據結構
查詢餘票:用戶輸入出發地、目的地、出發日三個條件,查詢可能存在的車次,用戶能夠看到每一個車次通過的站點名稱,以及每種座位的餘票數量。架構
購票:購票分爲訂票和付款兩個階段,本文重點分析訂票的模型設計和實現思路。併發
其實還有不少其餘的需求,好比給不一樣的車次設定銷售座位數配額,以及不一樣的區段設置不一樣的限額。我以爲這個需求不是核心最重要的訴求,因此,本文針對這個需求不作具體討論,也不是本文分析設計的重點。負載均衡
需求分析
確實,12306也是一個電商系統,並且看起來商品就是票了。由於若是把一張票當作是一個商品,那購票就相似於購買商品,而後每張票都有庫存,商品也有庫存的概念。可是若是咱們仔細想一想,會發現12306要複雜不少,由於咱們沒法預先肯定好全部的票,若是非要肯定,那隻能經過窮舉法了。分佈式
咱們知道一張票的本質是某個車次的某一段區間(一條線段),這個區間包含了若干個站點。而後咱們還發現,只要區間不重疊,那座位就不會發生競爭,能夠被回收利用,也就是說,能夠同時預先出售。高併發
另外,通過更深刻的分析,咱們還發現區間有4種關係:1)不重疊;2)部分重疊;3)徹底重疊;4)覆蓋;不重疊的狀況咱們已經討論過了,而覆蓋也是重疊的一種。因此咱們發現若是重疊,好比有兩個區間發生重疊,那重疊部分的區間(可能誇一個或多個站點)是在爭搶座位的。由於假設一列火車有100個座位,那每一個原子區間(兩個相鄰站點的連線),最多容許重疊99次。優化
因此,通過上面的分析,咱們知道了一個車次可以出售一張車票的核心業務規則是什麼?就是:這張車票所包含的每一個原子區間的重疊次數加1都不能超過車次的總座位數,實際上重疊次數+1也能夠理解爲線段的厚度。網站
模型設計
上面我分析了一下票的本質是什麼。那接下來咱們再來看看怎麼設計模型,來快速實現購票的需求,重點是怎麼設計商品聚合以及減庫存的邏輯。spa
架構設計
我以爲12306這樣的業務場景,很是適合使用CQRS架構;由於首先它是一個查多寫少、可是寫的業務邏輯很是複雜的系統。因此,很是適合作架構層面的讀寫分離,即採用CQRS架構。並且應該使用數據存儲也分離的CQRS。這樣CQ兩端才能夠徹底不須要顧及對方的問題,各自優化本身的問題便可。咱們能夠在C端使用DDD領域模型的思路,用良好設計的領域模型實現複雜的業務規則和業務邏輯。而Q端則使用分佈式緩存方案,實現可伸縮的查詢能力。
總結
本文徹底是憑本身對12306這個網站的核心業務的簡單思考而獲得的一些設計結果。若是真正的DDD領域建模,更多的是要和業務一線的工做人員、領域專家進行深刻溝通,才能更深刻的瞭解該領域內的業務知識,從而才能設計出更靠譜的領域模型和架構設計。我本人很是慚愧由於沒有上12306買過火車票,家離的比較近,就算要買也是家人給我買:)因此,本文所分享的內容不免是紙上談兵。但我以爲12306這個系統的業務確實比傳統的電商系統要複雜,且併發又這麼高。因此,我以爲這個系統真的很值得你們重視模型的設計,而不僅是隻關注技術層面的實現。