在微服務的設計模式裡,每個服務獨立資料庫 (Database per service) 是一種常用的設計模式。 (註1) 要採用這種模式,若搭配 SQL 資料庫,通常有三種作法:
選用 1, 2 帶來的額外開發成本會小得多,而 3 的話則有最完整的資源隔離。然而,無論服務的邊界切得如何完美,既然是同一個系統,往往多少都會有需要與其它服務的資料聯合起來一起查詢的時候。當這種需求發生時,1,2 這兩個解法依然允許 SQL join 直接執行在不同的服務對應的資料上。然而,如果選擇的作法是 3 的話,開發的成本就很高了。
在 Datomic ,並沒有提供類似資料表命名空間 (schema) 的語法,換言之,選項只有 1, 3 兩種。另一方面,Datalog 卻又提供語法,可以讓我們做到跨資料庫的查詢,換言之,如果是要實作微服務的設計模式,搭配 Datomic 資料庫的話,可以大方地採用 3 做為預設選項,而不太需要操心,將來暴增的查詢困難。
先來看個 Datalog 跨資料庫查詢的範例吧:
;; All revenue but orders in database b
[:find ?o ?r
:in $a $b
:where
[$a ?o :order/revenue ?r]
($b not [?o :order/revenue])]
在某公司,a 資料庫記錄了所有訂單對應的利潤。然而,凡是有退款、壞帳等疑慮的訂單,則會記錄在 b 資料庫裡。上頭的查詢是『找出 a 資料庫裡所有的訂單與對應的利潤,但是去除在 b 資料庫裡出現的訂單。』。
語法的變化只有出現在 :in
子句與 :where
子句,都增加了資料源變數 (source variable)。在 in
子句裡的 $a
和 $b
就是資料源變數,這種變數代表的是資料的來源,通常是一個資料庫,但是也可以是數組的陣列。在第一個 :where
子句裡的 $a
,它的意思是這個子句的比對只作用在 $a
資料庫;而第二個子句裡的 $b
,它的意思是這個子句的反向比對只作用在 $b
資料庫。
:in
子句後的變數整理一下,in
子句出現過的變數,目前為止已經介紹了三種:
?
起頭的命名。$
起頭的命名。%
。資料源變數主要的功能是限制子句作用的範圍。當只有一個資料源變數時,也就意謂著這不是跨資料庫查詢時,也就不用思考什麼範圍的問題了,因為作用的範圍就一定是預設的資料庫。也因此,Datalog 很貼心地設計成:「只有一個資料源變數時,資料源變數在子句裡可以省略。」
再仔細看觀察上方的範例,我們可以發現,資料源變數在 :where
子句裡的用法很一致。
not
子句。資料源變數要加在最左邊。但是,資料源變數還可以加在其它的子句裡嗎?比方說,如果我們想對規則 (rules) 也套用資料源變數呢?可以的,也是一樣加在最左邊。
在下方的例子,規則表達式子句 ($ actor-movie ?name ?movie-title)
就使用了資料源變數。
(def rules
'[[(actor-movie ?name ?title)
[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]]]])
(d/q '[:find ?name
:in $ % ?movie-title
:where ($ actor-movie ?name ?movie-title)]
db rules "The Terminator")
整理一下,資料源變數可以這樣子使用:
not
, not-join
, or
, or-join
子句。註: