問題二:實(shí)現(xiàn)跨越多個(gè)服務(wù)的事務(wù)
為強(qiáng)化應(yīng)用中的業(yè)務(wù)規(guī)則(也稱為不變式),傳統(tǒng)的整體應(yīng)用程序可依賴于 ACID事務(wù)性 。在網(wǎng)店的例子中,如果在客戶建立新訂單前,我們必須要去檢查該客戶的信用額度,這就必須確保多個(gè)并發(fā)的潛在訂單嘗試不會產(chǎn)生超過客戶信用額度的問題。如果Order類和Customer類使用同一個(gè)數(shù)據(jù)庫,我們可以輕易地將其實(shí)現(xiàn)為如下的ACID事務(wù)(具有適當(dāng)?shù)氖聞?wù)隔離度):
BEGIN TRANSACTION
…
SELECT ORDER_TOTAL
FROM ORDERS WHERE CUSTOMER_ID = ?
…
SELECT CREDIT_LIMIT
FROM CUSTOMERS WHERE CUSTOMER_ID = ?
…
INSERT INTO ORDERS …
…
COMMIT TRANSACTION
可惜在基于微服務(wù)的應(yīng)用中,我們并不能使用上面這種直接方法去維持?jǐn)?shù)據(jù)一致性。因?yàn)閿?shù)據(jù)庫表ORDERS和CUSTOMERS被不同的服務(wù)所有,并只能通過API進(jìn)行訪問。這兩個(gè)表甚至可能在不同的數(shù)據(jù)庫中。
對此,傳統(tǒng)的解決方案是 兩階段提交 (也稱為分布式交易),但對于當(dāng)代應(yīng)用而言,兩階段提交并非是一種可行的技術(shù)。依據(jù) CAP定理 ,應(yīng)用需要在可用性和一致性兩者間做出抉擇,通常首選可用性。進(jìn)一步講,一些當(dāng)代技術(shù)甚至都不支持ACID事務(wù),例如大多數(shù)NoSQL數(shù)據(jù)庫,更不用說兩階段提交了??紤]到維持?jǐn)?shù)據(jù)一致性的重要性,我們需要其它的解決方案。本文后面將給出這樣的解決方案,就是使用基于被稱為“事件溯源”的事件驅(qū)動架構(gòu)。
問題三:查詢與報(bào)表
維持?jǐn)?shù)據(jù)一致性并非是唯一的挑戰(zhàn),另一個(gè)問題是查詢與報(bào)表。在傳統(tǒng)的整體應(yīng)用程序中,編寫使用連接運(yùn)算的查詢是十分常見的。例如使用如下查詢,讀者能很輕易地查到近期網(wǎng)店的客戶及由他們所做的一定規(guī)模的訂單:
SELECT *
FROM CUSTOMER c, ORDER o
WHERE
c.id = o.ID
AND o.ORDER_TOTAL > 100000
AND o.STATE = 'SHIPPED'
AND c.CREATION_DATE > ?
但這類查詢并不能用于基于微服務(wù)的網(wǎng)店中。正如前面所提及的,數(shù)據(jù)庫表ORDERS和CUSTOMERS被不同的服務(wù)所擁有,并只能通過API訪問。甚至可能有一些服務(wù)不會去采用SQL數(shù)據(jù)庫。后文中讀者將看到在一些其它服務(wù)中,使用了稱為一種“ 時(shí)間溯源 ”的方法,使得查詢更加具有挑戰(zhàn)性。稍后,讓我們看一下在為實(shí)現(xiàn)微服務(wù)而進(jìn)行基于領(lǐng)域模型的業(yè)務(wù)邏輯開發(fā)時(shí),領(lǐng)域驅(qū)動設(shè)計(jì)是如何成為一種不可或缺的工具的。
DDD聚合是微服務(wù)的構(gòu)建模塊
正如讀者所看到的,為成功地使用微服務(wù)架構(gòu)開發(fā)業(yè)務(wù)應(yīng)用,有一些問題必須要得到解決。在Eric Evans所著的經(jīng)典書籍《 領(lǐng)域驅(qū)動設(shè)計(jì) 》中,可為其中的一些問題找到解決方法。該書出版于2003年,描述了復(fù)雜軟件的設(shè)計(jì)方法,對于微服務(wù)開發(fā)是十分有用的。尤其是,該書指出領(lǐng)域驅(qū)動設(shè)計(jì)可用于創(chuàng)建跨服務(wù)分區(qū)的模塊化領(lǐng)域模型。
什么是聚合?
Evans為領(lǐng)域驅(qū)動設(shè)計(jì)中的領(lǐng)域模型定義了一系列的構(gòu)建模塊。一些構(gòu)建模塊已成為日常開發(fā)語言的組成部分,其中包括:實(shí)體,即具有持久標(biāo)識對象;值對象,即雖然沒有標(biāo)識但是由自身屬性所定義的對象;服務(wù),包含了不屬于任何實(shí)體或值對象服務(wù)的業(yè)務(wù)邏輯;倉庫,表示一系列的持久實(shí)體。除純粹領(lǐng)域驅(qū)動設(shè)計(jì)主義者之外,聚合作為其中的一種構(gòu)件卻幾乎被所有的開發(fā)人員所忽視。但是對于微服務(wù)開發(fā),聚合已被證實(shí)是十分關(guān)鍵的。
聚合是可被視為同一整體的一組領(lǐng)域?qū)ο蟆>酆嫌筛鶎?shí)體以及一個(gè)或多個(gè)其它關(guān)聯(lián)視圖和值對象所組成。例如,網(wǎng)店的領(lǐng)域模型包括了Order和Customer等領(lǐng)域?qū)ο?。Order聚合的組成包括Order實(shí)體(即根實(shí)體)和一個(gè)或多個(gè)的OrderLineItem值對象,還有DeliveryAddress和PaymentInformation等一些其它的值對象。Customer聚合的組成包括Customer根實(shí)體以及DeliveryInfo和PaymentInformation等一些其它的值對象。

領(lǐng)域模型可用聚合分解成組塊(chunk),每個(gè)組塊自身是易于被理解的。這樣就澄清了加載和刪除等操作的范疇問題。聚合通常全部從數(shù)據(jù)庫加載。刪除一個(gè)聚合也就刪除了其所有的對象。但是使用聚合的優(yōu)點(diǎn)遠(yuǎn)遠(yuǎn)超出了領(lǐng)域模型的模塊化。這是因?yàn)榫酆媳仨氁袷卮_定的規(guī)則。