為什么我們不用數(shù)據(jù)庫生成 ID?
先介紹一下背景???
團(tuán)隊正在一個為 SQL Server 構(gòu)建數(shù)據(jù)目錄項目的歷程中,我們優(yōu)化系統(tǒng)以實現(xiàn)解耦。這對我們來說非常重要,從根本上來說,我歸結(jié)為兩個核心原則,希望每個軟件專業(yè)人員都能認(rèn)同:
我們不希望系統(tǒng)復(fù)雜度隨功能的增加而線性增長,這樣會大大拖慢我們在業(yè)務(wù)發(fā)展速度以及對于價值的信心。
我們希望能夠優(yōu)先從客戶需求、訪問性能、查詢模式、業(yè)務(wù)變化等方面考慮,能夠適應(yīng)不斷發(fā)展的需求和需要。換句話說,我們希望能夠?qū)⑾到y(tǒng)內(nèi)的任何組件換成更合適的組件,以滿足當(dāng)前而不是過去的需求。
下面是 protoactor-go 開源項目里的一句話 “software should be composed, not built”,與我要在本文陳述的觀點非常相似:

對持久化有何影響???
考慮到前面總體原則,我們不想把自己的狀態(tài)持久化耦合到一個特定的數(shù)據(jù)庫引擎上。從實際情況來看,就是說不將持久化的具體關(guān)注點傳遞到領(lǐng)域?qū)印V砸獙崿F(xiàn)這一點,因為我們今天對規(guī)則的認(rèn)知可能會讓我們依賴某種具體數(shù)據(jù)庫技術(shù),比如 SQL Server,但并不能確保它能滿足未來的能力需求。
有了這個具體的要求,持久化就需要出現(xiàn)在域事件而不是存儲系統(tǒng)中,這也導(dǎo)致不同的存儲需求。
幸運的是,有一些廣為人知的、經(jīng)過實戰(zhàn)檢驗的模式可以解決這個問題,如結(jié)合 CQRS 領(lǐng)域驅(qū)動設(shè)計中的聚合設(shè)計等。因此這里的假設(shè)是,實現(xiàn)理想狀態(tài)對我們來說是低成本的。
代碼生成 ID 還是數(shù)據(jù)庫生成 ID ??
許多數(shù)據(jù)存儲系統(tǒng)(如 SQL Server)都具備為每條記錄(如行、文檔等)生成唯一標(biāo)識符的方法。當(dāng)將新記錄插入表中時,使用自動增量鍵可以生成唯一編號。當(dāng)我們要插入一條新記錄時,數(shù)據(jù)庫引擎就自動完成自增主鍵,并且可以確保該鍵對于表是唯一的。
但是如果依賴數(shù)據(jù)庫來為我們業(yè)務(wù)域生成唯一標(biāo)識符,這將讓業(yè)務(wù)層與數(shù)據(jù)存儲系統(tǒng)耦合在一起。如果我們想切換一個不支持生成自動增量主鍵的數(shù)據(jù)存儲系統(tǒng),那就不能簡單更換。而且,每個數(shù)據(jù)存儲系統(tǒng)生成標(biāo)識符的方式都不一樣,這可能會導(dǎo)致我們最終使用不同的主鍵類型。除此之外,這些類型的生成方式可能不適合分布式系統(tǒng)。例如,當(dāng)我們在 SQL Server 數(shù)據(jù)庫中擁有一個生成主鍵的表時,我們沒有一個簡單的方法在分布式環(huán)境中水平擴(kuò)展這個表。
通過讓業(yè)務(wù)域的消費者(即通過命令和查詢與域通信的傳輸層)負(fù)責(zé)唯一鍵的標(biāo)識符生成,就可以克服這些問題。它減少對環(huán)境的依賴性,又反過來讓我們不用依賴數(shù)據(jù)庫來生成 Id。這種方法還有一個好處:它可以支持分布式。例如,我們可以將一個表分區(qū)到兩個物理 SQL Server上,并分擔(dān)查詢的請求。如果我們有一個自動增量的字段,這在 SQL Server 上就不行了。
我們決定做什么???
基于這些事實,我們決定讓業(yè)務(wù)域?qū)觼砩捎蚣系臉?biāo)識符。我們在域?qū)觾?nèi)將這些標(biāo)識符表示為 64 位無符號整數(shù)。域消費者可以根據(jù)自己的上下文自由決定應(yīng)該用什么表示方式(例如ASP.NET Core MVC 可以將標(biāo)識符序列化為字符串,以便于其客戶端消費資源對象等)。
為什么要 64 位整型,而不是 UUID?
最后,您可能想知道為什么使用64位整數(shù)。此處的主要目的是使我們能夠甚至跨聚合根生成唯一的標(biāo)識符。考慮到幾乎每個平臺都有對應(yīng)的 API,UUID 是非常方便的調(diào)用方式(例如 .NET 中 Guid.NewGuid() 等)。UUID 最大的問題是存儲和索引方面的成本與開銷,雖然對我們來說不是大問題。不過,在分布式系統(tǒng)中,已經(jīng)有一些成熟的方法來生成唯一標(biāo)識符作為更高效的主鍵類型,比如使用 64 位整數(shù)。Twitter Snowflake (1) 算法就是其中之一,這讓我們選擇了 64 位整數(shù)而不是 UUID。我們使用的是 Rob Janssen 的開源 IdGen (2) 庫,它是一個適用于 .NET 的 Twitter Snowflake 類型的 ID 生成器。
(1)?https://developer.twitter.com/en/docs/basics/twitter-ids.html
(2) https://github.com/RobThree/IdGen
關(guān)注公眾號【Java技術(shù)江湖】后回復(fù)“PDF”即可領(lǐng)取200+頁的《Java工程師面試指南》
強(qiáng)烈推薦,幾乎涵蓋所有Java工程師必知必會的知識點,不管是復(fù)習(xí)還是面試,都很實用。

