閒談軟體設計:Query Object

更新於 發佈於 閱讀時間約 7 分鐘

前言

之前在開發旅遊 app 的內容管理平台時,初期 Repository 用得好好的,但到後期,為了輸出報表或是提供特定列表時,就需要一直在 repository 的介面上加上特定的 query method,像是這樣 (這不是當時的程式,只是示意):

一下是取得某份文件所有語系副本,一下又是取得某文件的特定語係副本,又或者是取得某份文件參考到的其他文件,就這樣,DocumentRepository 的 method 越來越多。

當時根據資料 scale 和特性,用不同的資料庫儲存,像是比較類似 metadata 的資料,例如帳號、權限管理,使用傳統的 RDBMS 儲存,資料量會比較龐大,沒什麼結構的資料則是用 NoSQL 的儲存,這時 repository 的抽象層確實提供一個好處,business logic 並不需要知道實際資料放在何處,操作的方式在換資料庫後也完全不用變。

查詢條件物件化

說是這麼說啦,但 repository 內部的實作就有點頭痛了,同樣的查詢條件,在換資料庫後得重寫,於是我決定替 Repository 加入一個 QueryDesciptor 物件描述查詢條件,說真的,那時還沒有看過《Patterns of Enterprise Application Architecture》的 Query Object (這本書一直沒全部看完),當初的動機很單純,只是想包裝 Hibernate 的 Criteria API,可以用類似的物件導向語法寫 query 條件,然後由 repository 轉換成底層對應的 query 方式,如此 business logic layer 不需要根據不同的資料庫寫不同的 query。

因此上面例子中的 DocumentRepository 就不再需要多加三個 method,而是在 GenericRepository 中加入對 QueryDescriptor 的支援,為什麼 method 要叫 filter,而不是 find 或是其他的,主要是最近各大語言 (例如Swift) 的 collection 介面都把類似的行為用 filter 來完成。

這麼做之後,原本寫在MongoDocumentRepository 的 query 省下來了,太好了,少寫很多程式,ㄜ~ QueryDescriptor 沒那麼神,只是變成 query 搬到 DocumentManager 的實作中 (例子中的 originId、language 和 referedId 是 Document 物件的 property 名稱,不是 database 的 column 名稱):

也就是說,假設要換資料庫,只要注入 (inject) 不同的 repository 實作,邏輯是不需要更改的,聽起來很棒,但當初實作 QueryDescriptor 對 Hibernate Criteria 及對 MongoDB 搜尋條件的轉換時吃了不少苦頭,足足花上了一個禮拜的時間,而且還是最簡單的版本。

若不考慮 DSL 的寫法,可能會快一點,但為了讓用起來很像在寫 query language,得花上不少時間包裝,where 後面能接 is、in、like、greatThan、 lessThan、 greatThenOrEqual、lessThanOrEqual 等常見條件,加上 all 和 any 設定條件是全部滿足 (AND) 或是任一滿足 (OR),在 aggregation 的部分只有 min、max 和 count,最後再加上 sort、from 和 size 處理分頁的問題,projection 當時沒用到,就沒有加進去。

Figure 1 - Query object and the visitor

Figure 1 - Query object and the visitor

內部其實是一個 Query 的 AST 樹狀結構 (上述很多 method 只是建立一個物件然後呼叫 add 加到目前的節點), 當時轉換時是寫個 Visitor 走遍這個 AST 然後根據當下的節點產生對應的物件,Hibernate 應該是較簡單的,像是 is 其實是用 Restrictions.eq(name, value) 產生一個 SimpleExpression 物件,在處理 MongoDB 時就比較討厭一點,不過最花時間的,是測試,畢竟轉換出錯,撈出來的資料就會是錯的,測試案例寫了不少。

至於花這麼多時間真的值得嗎?畢竟有原生的 API 可以做, 特別是如果有用 Spring Data JPA 搭配 annotation,只要繼承 Spring Data 的 Repository 然後使用 Spring 提供的 naming convention 設計 method,Spring Data 就會自動在 runtime 提供實作 (這其實真的蠻神的),但我自己覺得值得

稍微複雜一點的 query 其實代表著某些商業邏輯,為了避免相依 (這時候不在意相依的人就開心了,對吧~在意這麼多幹嘛,直接在 business logic layer 用第三方 API 寫 query 就好啦),必須把這一段程式 push 到 repository 的實作層,會變成這些商業邏輯被隱藏起來了,如果有個好的描述語言,像剛剛 DefaultDocumentManager 的例子,我倒覺得很好讀,也可以清楚知道背後的商業邏輯是什麼,是很好的一件事。

只是沒想到最近在整理一些 Repository 的心得時,再次拿起《Patterns of Enterprise Application Architecture》來看時,才發現原來這是一個稱作 Query Object 的 pattern,而 Hibernate Criteria 或是 MongoDB 的 Filters 都是各家廠商在他們自己產品中提供的 Query Object,我做的只是透過抽象避免 vendor lock-in 而已。

結語

最後,以 PoEAA 書中對 Query Object 的描述來總結

An object that represents a database query.

本來這一篇是要放入 閒談軟體設計:Repository 的內容,但覺得有點長,於是獨立出來了!


留言
avatar-img
留言分享你的想法!
avatar-img
Spirit的沙龍
53會員
105內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
Spirit的沙龍的其他內容
2024/03/23
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
2024/03/23
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
2024/03/09
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
2024/03/09
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
2024/03/02
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
2024/03/02
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
看更多
你可能也想看
Thumbnail
沙龍一直是創作與交流的重要空間,這次 vocus 全面改版了沙龍介面,就是為了讓好內容被好好看見! 你可以自由編排你的沙龍首頁版位,新版手機介面也讓每位訪客都能更快找到感興趣的內容、成為你的支持者。 改版完成後可以在社群媒體分享新版面,並標記 @vocus.official⁠ ♥️ ⁠
Thumbnail
沙龍一直是創作與交流的重要空間,這次 vocus 全面改版了沙龍介面,就是為了讓好內容被好好看見! 你可以自由編排你的沙龍首頁版位,新版手機介面也讓每位訪客都能更快找到感興趣的內容、成為你的支持者。 改版完成後可以在社群媒體分享新版面,並標記 @vocus.official⁠ ♥️ ⁠
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
這本書在書單中很久了,前陣子有空繞去天龍書局發現有中譯版就買回家來翻翻 (沒錯,買中譯版 XD),一看很合胃口,很快就翻完了,心有戚戚焉的句子很多,特別是在最後一章關於框架設計的部分。
Thumbnail
這本書在書單中很久了,前陣子有空繞去天龍書局發現有中譯版就買回家來翻翻 (沒錯,買中譯版 XD),一看很合胃口,很快就翻完了,心有戚戚焉的句子很多,特別是在最後一章關於框架設計的部分。
Thumbnail
稍微複雜一點的 query 其實代表著某些商業邏輯,若把這一段程式放到 repository 的實作層,會變成這些商業邏輯被隱藏起來了,如果有個好的描述語言,我倒覺得很好讀,也可以清楚知道背後的商業邏輯是什麼,是很好的一件事。
Thumbnail
稍微複雜一點的 query 其實代表著某些商業邏輯,若把這一段程式放到 repository 的實作層,會變成這些商業邏輯被隱藏起來了,如果有個好的描述語言,我倒覺得很好讀,也可以清楚知道背後的商業邏輯是什麼,是很好的一件事。
Thumbnail
簡單說,Repository 提供像是 array 或是 dictionary 的容器,對程式來說,就好像記憶體中有所有的物件,不用去想物件其實是從資料庫來的。作法是在 data mapping layer 之上再提供一個 collection 的介面來存取物件,將資料庫的細節從商業邏輯層抽離。
Thumbnail
簡單說,Repository 提供像是 array 或是 dictionary 的容器,對程式來說,就好像記憶體中有所有的物件,不用去想物件其實是從資料庫來的。作法是在 data mapping layer 之上再提供一個 collection 的介面來存取物件,將資料庫的細節從商業邏輯層抽離。
Thumbnail
MongoDB非常擅長查詢大量的數據並經常更新這些資訊, 在多數的情況之下, 我們只要查詢資訊最新的狀態, 那假設我們需要查詢資料的上一個狀態呢? 如果我們需要一些文檔版本控管功能時怎麼辦呢? 這就是我們可以使用版本控管設計模式的地方了。 這個模式之下會保存文檔的歷史版本, 我們就不用導入另外一個版
Thumbnail
MongoDB非常擅長查詢大量的數據並經常更新這些資訊, 在多數的情況之下, 我們只要查詢資訊最新的狀態, 那假設我們需要查詢資料的上一個狀態呢? 如果我們需要一些文檔版本控管功能時怎麼辦呢? 這就是我們可以使用版本控管設計模式的地方了。 這個模式之下會保存文檔的歷史版本, 我們就不用導入另外一個版
Thumbnail
介紹 在過去的歷史裡開發程式都是以呼叫Function的方式來做程式開發,後來才有了物件導向設計的概念,而物件導向程式設計的概念就是,使用【物件】的方式來設計程式。 Q: 什麼是【物件】? A:【物件】會包含以下這四個特性: 抽象(Abstraction) 封裝(Encapsulation)
Thumbnail
介紹 在過去的歷史裡開發程式都是以呼叫Function的方式來做程式開發,後來才有了物件導向設計的概念,而物件導向程式設計的概念就是,使用【物件】的方式來設計程式。 Q: 什麼是【物件】? A:【物件】會包含以下這四個特性: 抽象(Abstraction) 封裝(Encapsulation)
Thumbnail
大數據的時代,如何有效率的搜尋來找到我們要的答案?這時候就要透過一些資訊檢索的技術來幫我們提升效率了,而本篇會介紹索引的基本概念以及為什麼演化至倒排索引,為我們帶來了哪些好處? 什麼是索引? 那什麼又是倒排索引? 能解決什麼問題呢? 為什麼更快更精準? 結語
Thumbnail
大數據的時代,如何有效率的搜尋來找到我們要的答案?這時候就要透過一些資訊檢索的技術來幫我們提升效率了,而本篇會介紹索引的基本概念以及為什麼演化至倒排索引,為我們帶來了哪些好處? 什麼是索引? 那什麼又是倒排索引? 能解決什麼問題呢? 為什麼更快更精準? 結語
Thumbnail
不知不覺寫到第七篇了!QUERY 真的有好多好多東西可以說 (ノ>ω<)ノ QUERY 其實還能處理有日期、時間的資料,而且語法也相當容易,和我們之前就看過的聚集函式很像。你如果會了之前的聚集函式,相信這次處理日期和時間也會對你來說很簡單!
Thumbnail
不知不覺寫到第七篇了!QUERY 真的有好多好多東西可以說 (ノ>ω<)ノ QUERY 其實還能處理有日期、時間的資料,而且語法也相當容易,和我們之前就看過的聚集函式很像。你如果會了之前的聚集函式,相信這次處理日期和時間也會對你來說很簡單!
Thumbnail
軟體開發一個很迷人的地方是可以在架空的世界(電腦世界)中重新思考、解構並處理真實世界的問題。但要怎樣真正有效的解決問題就很看各家功力了。 這篇文章我們暫且放下溝通及流程規劃的議題,聚焦來看看純粹領域差異造成的困難以及該怎麼面對。 回顧過往曾經觸碰過的領域真的滿多,茲列舉幾個
Thumbnail
軟體開發一個很迷人的地方是可以在架空的世界(電腦世界)中重新思考、解構並處理真實世界的問題。但要怎樣真正有效的解決問題就很看各家功力了。 這篇文章我們暫且放下溝通及流程規劃的議題,聚焦來看看純粹領域差異造成的困難以及該怎麼面對。 回顧過往曾經觸碰過的領域真的滿多,茲列舉幾個
Thumbnail
數據分析的好,人人都知道。可以改善用戶體驗、找到肉眼無法注意的機會點、尋找新的商機、可以讓資源花在刀口上等等的族繁不及備載,所以大家會努力想著要找到能數據分析的人、方法、設備,讓自己的公司能夠藉著數據分析殺出一條活路、或是開出一條別人看不見的賽道。
Thumbnail
數據分析的好,人人都知道。可以改善用戶體驗、找到肉眼無法注意的機會點、尋找新的商機、可以讓資源花在刀口上等等的族繁不及備載,所以大家會努力想著要找到能數據分析的人、方法、設備,讓自己的公司能夠藉著數據分析殺出一條活路、或是開出一條別人看不見的賽道。
Thumbnail
框架,是思考、表達與問題解決的架構。善用商業框架與發想框架,就能發揮垂直與水平思考的綜效。
Thumbnail
框架,是思考、表達與問題解決的架構。善用商業框架與發想框架,就能發揮垂直與水平思考的綜效。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News