Orm 仇恨

2012 年 5 月 8 日

幾個月前我在倫敦參加 QCon 會議時,似乎每場演講都對物件/關係對應 (ORM) 工具發表了一些尖酸刻薄的評論。我想我應該更仔細閱讀寄給講者的會議電子郵件,裡面肯定有告訴我們每 45 分鐘至少要對 ORM 鄙夷一次的內容。但正如你所見,我想對這種 ORM 仇恨反擊一下——因為我認為其中很多都是不合理的。

對 ORM 的指控可以總結為它們很複雜,而且只提供了一個關係資料儲存庫的漏洞抽象。它們的複雜性意味著艱難的學習曲線,而且使用 ORM 的系統通常執行不良——通常是因為與底層資料庫的互動過於天真。

這些指控有很多真實性,但這些指控遺漏了一個重要的脈絡。物件/關係對應問題是困難的。你所做的本質上是在兩個截然不同的資料表示之間同步,一個在關係資料庫中,另一個在記憶體中。儘管這通常稱為物件關係對應,但這裡實際上與物件無關。它理應稱為記憶體/關係對應問題,因為它適用於將 RDBMS 對應到任何記憶體中資料結構。記憶體中資料結構比關係模型提供了更大的靈活性,因此為了有效編寫程式,大多數人希望使用更多樣化的記憶體中結構,因此面臨著將其對應回資料庫的關係。

對應進一步複雜化,因為你可以對任一側進行必須對應到另一側的變更。由於可以有多人同時存取和修改資料庫,因此會出現更多複雜性。ORM 必須處理這種並行性,因為你不能只依賴交易——在大多數情況下,你無法在記憶體中修改資料時保持交易開啟。

我認為,如果你要像許多人對 ORM 所做的那樣抨擊某事,你必須說明替代方案。你用什麼來代替 ORM?我通常聽到的廉價攻擊忽略了這一點,因為這會變得一團糟。基本上歸結為兩種策略,以不同的(更好的)方式解決問題,或避免問題。這兩種方法都有顯著的缺陷。

更好的解決方案

聽聽某些批評者的說法,你會以為現代軟體開發人員最好的做法就是自己開發 ORM。這表示像 Hibernate 和 Active Record 這樣的工具已經變成臃腫軟體,所以你應該提出自己的輕量級替代方案。現在,我花了很多時間抱怨臃腫軟體,但 ORM 真的不符合這個條件 - 我帶著痛苦的記憶這麼說。在 90 年代的大部分時間裡,我看到一個又一個專案透過撰寫自己的架構來處理物件/關聯式對應問題 - 這總是比人們想像的困難得多。通常你會獲得足夠的早期成功來深入承諾這個架構,但過了一段時間後你才會發現自己陷入泥沼 - 這是讓我非常認同 Ted Neward 的名言之處,他說物件關聯式對應是電腦科學的越南[1]

廣泛提供的開源 ORM(例如 iBatis、Hibernate 和 Active Record)在消除這個問題上發揮了很大作用[2]。當然它們不是簡單易用的工具,就像我說的,底層問題很困難,但你不必處理撰寫這些東西的完整體驗(恐怖,恐怖)。無論你有多麼討厭使用 ORM,請相信我 - 你還是比較好過。

我常常覺得對 ORM 的許多挫折感都是來自於過高的期望。許多人對待關聯式資料庫「就像一個被關在閣樓裡、沒人想和她說話的瘋阿姨」[3]。在這個世界觀中,他們只想處理記憶體中的資料結構,並讓 ORM 處理資料庫。這種想法對於小型應用程式和負載來說可能有效,但一旦情況變得困難,它就會很快瓦解。基本上,ORM 可以處理大約 80-90% 的對應問題,但最後一部分總是需要真正了解關聯式資料庫運作方式的人員仔細處理。

這就是批評 ORM 是有缺陷的抽象之處。這是真的,但並非一定要避免它們。對應到關聯式資料庫涉及大量重複的樣板程式碼。一個能讓我避免 80% 的架構是值得的,即使它只有 80%。問題出在我假裝它是 100%,而實際上並非如此。Active Record 成名的 David Heinemeier Hansson 一直主張,如果你正在撰寫由關聯式資料庫支援的應用程式,你最好知道關聯式資料庫是如何運作的。Active Record 就是基於這個想法設計的,它負責處理無聊的事情,但提供了人孔,以便你在需要時可以使用 SQL。這是一個更佳的方法來思考 ORM 應該扮演的角色。

這樣對 ORM 應有的功能所抱持的有限期待,會帶來後果。我常聽人抱怨,他們被迫犧牲物件模型,讓它更具關聯性,以迎合 ORM。實際上,我認為這是使用關聯式資料庫的必然結果 - 你必須讓你的記憶體中模型更具關聯性,否則就會讓你的對應碼變得複雜。我認為,為了簡化你的物件關聯對應,擁有更具關聯性的網域模型是完全合理的 [4]。這並不表示你應該永遠精確地遵循關聯模型,但這表示你會將對應複雜性納入你的網域模型設計中。

那麼,我是不是在說你應該永遠使用現有的 ORM,而不是自己做些事情?嗯,我已經學到永遠不要說「永遠」。一個浮現在我腦海中的例外情況是,當你只從資料庫中讀取時。ORM 很複雜,因為它們必須處理雙向對應。單向問題容易處理得多,特別是如果你的需求不太複雜,而且你對 SQL 很熟悉。這是 CQRS 的論點之一。

所以,大多數時候,對應都是一個複雜的問題,而且你最好使用一個公認的複雜工具,而不是在亞洲發動一場陸上戰爭。但是,還有我先前提到的第二個替代方案 - 你能避開這個問題嗎?

避開問題

要避開對應問題,你有兩個替代方案。你可以使用記憶體中的關聯模型,或者不要在資料庫中使用它。

在記憶體中使用關聯模型基本上表示以關聯的方式編寫程式,貫穿你的應用程式。在許多方面,這就是 90 年代的 CRUD 工具所提供的。它們非常適合於你只是將資料推送到螢幕並返回的應用程式,或者你的邏輯以 SQL 查詢的方式表達得很好的應用程式。有些問題很適合這種方法,所以如果你能這樣做,你應該這麼做。但它的缺點是,你常常不能這麼做。

當談到不在磁碟上使用關聯式資料庫時,會出現一群新的擁護者和舊的回憶。在 90 年代,我們許多人(是的,包括我)認為物件資料庫會透過消除磁碟上的關聯來解決問題。我們都知道結果如何。但是,現在有新的 NoSQL 資料庫 - 它們會讓我們巧妙地避開 ORM 困境,並讓我們對我們的資料儲存感到驚訝嗎?

正如你可能已經了解的,我認為 NoSQL 是非常值得認真看待的技術。如果你有一個應用程式問題很適合 NoSQL 資料模型 - 例如 聚合 或圖形 - 那麼你可以完全避開對應的麻煩。事實上,這通常是我聽過團隊使用 NoSQL 解決方案的原因。我認為,這是一個可行的途徑 - 因此我有興趣增加我們對 NoSQL 系統的了解。但即便如此,它也只在應用程式模型和 NoSQL 資料模型之間的配合良好的時候才有效。並非所有問題在技術上都適合 NoSQL 資料庫。當然,在許多情況下,你無論如何都只能使用關聯模型。也許這是一個你無法跳過的公司標準,也許你無法說服你的同事接受不成熟技術的風險。在這種情況下,你無法避開對應問題。

因此,ORM 協助我們處理大多數企業應用程式的實際問題。的確,它們經常被誤用,而且有時可以避免潛在問題。它們並非美觀的工具,但它們所解決的問題也不太討喜。我想它們應該獲得更多尊重和理解。

備註

1: 我必須承認,對於越南類比,我有一種深深的衝突感。在某種程度上,它似乎是軟體開發問題的悲慘誇大,將棘手的技術比作戰爭。程式設計可能很討厭,但你仍然坐在相對舒適的椅子上,通常有空調,而且除錯並不會讓你面對子彈。但在另一個層面上,這個詞組確實引起了陷入泥潭的感覺。

2: 也有商業 ORM,例如 TOPLink 和 Kodo。但開放原始碼工具的可近性讓它們成為主流。

3: 我很喜歡 這句話,我忍不住要重複使用它。

4: 確實,在許多情況下,你根本不應該考慮建立行為豐富的網域模型,而應該使用 資料列閘道,它們只是用簡單物件包裝的關聯式資料表。