日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何基于 DDD 构建微服务?

發(fā)布時間:2023/12/4 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何基于 DDD 构建微服务? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文將討論微服務(wù)與 DDD 涉及到的概念、策劃和設(shè)計方法,并且嘗試將一個單體應(yīng)用拆分成多個基于 DDD 的微服務(wù)。

微服務(wù)的定義

微服務(wù)中的“微”雖然表示服務(wù)的規(guī)模,但它并不是使應(yīng)用程序成為微服務(wù)的唯一標準。當團隊轉(zhuǎn)向基于微服務(wù)的架構(gòu)時,他們的目標是提高敏捷性,即自主且頻繁地部署功能。

因此,很難給微服務(wù)架構(gòu)風(fēng)格下一個簡單的定義。我喜歡 Adrian Cockcroft 關(guān)于微服務(wù)的簡短定義:“面向服務(wù)的架構(gòu)由具有界限上下文、松散耦合的元素組成。”

盡管這定義了一種高級的設(shè)計啟發(fā)式方法,但微服務(wù)架構(gòu)具有的特性,使其有別于以往的面向服務(wù)架構(gòu)。根據(jù)以往的文章,我們總結(jié)了微服務(wù)架構(gòu)應(yīng)具備的一些特征:

  • 服務(wù)以業(yè)務(wù)上下文為中心定義了良好的邊界,而不是以任意的技術(shù)抽象為中心;

  • 隱藏實現(xiàn)細節(jié),并通過意圖接口暴露功能;

  • 服務(wù)不會共享超出其邊界的內(nèi)部結(jié)構(gòu),例如不共享數(shù)據(jù)庫;

  • 服務(wù)具有故障快速恢復(fù)能力;

  • 團隊職能獨立,能夠自主發(fā)布變更;

  • 團隊擁護自動化文化,例如自動化測試、持續(xù)集成和持續(xù)交付。

  • 簡而言之,我們可以將這種架構(gòu)風(fēng)格總結(jié)如下:

    松散耦合的面向服務(wù)的架構(gòu),其中每個服務(wù)都封裝在定義良好的界限上下文中,支持應(yīng)用程序快速、頻繁且可靠的交付。

    領(lǐng)域驅(qū)動設(shè)計和界限上下文

    微服務(wù)的強大之處在于清晰地定義了它們的職責(zé)并劃定了它們之間的邊界。它的目的是在邊界內(nèi)建立高內(nèi)聚,在邊界外建立低耦合。也就是說,傾向于一起改變的事物應(yīng)該放在一起。正如現(xiàn)實生活中的許多問題一樣,但這說起來容易做起來難,業(yè)務(wù)在不斷發(fā)展,設(shè)想也隨之改變。因此,重構(gòu)能力是設(shè)計系統(tǒng)時考慮的另一項關(guān)鍵問題。

    在我們看來,領(lǐng)域驅(qū)動設(shè)計 (DDD) 是關(guān)鍵,它是設(shè)計微服務(wù)時必不可少的工具,無論是對單體應(yīng)用進行拆分還是從頭開始構(gòu)建一個新項目。領(lǐng)域驅(qū)動設(shè)計因 Eric Evans 的著作而出名,它是一組思想、原則和模式,可以幫助我們基于業(yè)務(wù)領(lǐng)域的底層模型設(shè)計軟件系統(tǒng)。開發(fā)人員和領(lǐng)域?qū)<乙黄鹗褂媒y(tǒng)一的通用語言創(chuàng)建業(yè)務(wù)模型。然后將這些模型綁定到有意義的系統(tǒng)上,在這些系統(tǒng)和處理這些服務(wù)的團隊之間建立協(xié)作協(xié)議。更重要的是,它們設(shè)計了系統(tǒng)之間的概念輪廓或邊界。

    微服務(wù)設(shè)計從這些概念中汲取了靈感,因為所有這些原理都有助于構(gòu)建可以獨立變更和發(fā)展的模塊化系統(tǒng)。

    在繼續(xù)深入之前,讓我們快速瀏覽一下 DDD 的一些基本術(shù)語。對領(lǐng)域驅(qū)動設(shè)計的完整概述不在本文的討論范圍之內(nèi)。

    領(lǐng)域(Domain): 代表組織所做的工作。例如零售或電子商務(wù)。

    子域(Subdomain): 組織或組織內(nèi)的業(yè)務(wù)部門。一個領(lǐng)域由多個子域組成。

    統(tǒng)一語言(Ubiquitous language):這是用于表達模型的語言。在下面的例子中(圖 1),Item 是一個模型,它是每個子域的統(tǒng)一語言。開發(fā)人員、產(chǎn)品經(jīng)理、領(lǐng)域?qū)<液蜆I(yè)務(wù)各涉眾方都能就使用這種語言達成一致,并在他們的工件(代碼、產(chǎn)品文檔等)中使用該語言。

    圖 1:電子商務(wù)領(lǐng)域中的子域和界限上下文

    界限上下文(Bounded Contexts):領(lǐng)域驅(qū)動設(shè)計將界限上下文定義為“一個單詞或語句出現(xiàn)時確定其含義的設(shè)置”。簡而言之,這意味著模型在邊界內(nèi)是有含義的。在上面的例子中(圖 1),“Item”在每個上下文中都有不同的含義。在 Catalog 上下文中,Item 表示可出售的產(chǎn)品,而在 Cart 上下文中,它表示客戶已添加到購物車中的商品選項。在 Fulfillment 上下文中,它表示將要運送給客戶的倉庫物料。這些模型各不相同,每個模型都有不同的含義,并且可能包含不同的屬性。通過將這些模型分離并將其隔離在各自的邊界內(nèi),我們就可以自由地表達這些模型,而不會產(chǎn)生歧義。

    注意: 必須理解子域和界限上下文之間的區(qū)別。子域?qū)儆趩栴}空間,即我們的業(yè)務(wù)要如何看待問題,而界限上下文屬于解決方案空間,即我們將如何實施問題的解決方案。理論上,每個子域可能有多個界限上下文,盡管我們努力每個子域只提供一個界限上下文。

    微服務(wù)和界限上下文如何關(guān)聯(lián)

    現(xiàn)在,微服務(wù)適用于哪些地方?每個界限上下文都能映射到對應(yīng)的微服務(wù)嗎?不一定。我們來看看原因。在某些情況下,界限上下文的邊界或輪廓可能會非常大。

    圖 2:界限上下文和微服務(wù)

    考慮上面的例子。定價界限上下文有三個不同的模型:價格(Price)、定價項(Priced items) 和折扣(Discounts),分別負責(zé)目錄項的價格、計算列表項的總價以及各自使用的折扣。我們可以創(chuàng)建一個包含上述所有模型的單一系統(tǒng),但它可能是一個不合理的大型應(yīng)用程序。如前所述,每個數(shù)據(jù)模型都有其不變性和業(yè)務(wù)規(guī)則。隨著時間的推移,如果我們不小心的話,這個系統(tǒng)就可能會變成一個大泥球,界限模糊,職責(zé)重疊,甚至很可能會回到我們開始的地方——單體應(yīng)用。

    對這個系統(tǒng)建模的另一種方法是將相關(guān)模型分離或分組到單獨的微服務(wù)中。在 DDD 中,這些模型(價格、定價項和折扣)被稱為聚合(Aggregates)。聚合是由相關(guān)模型組成的自包含模型。我們只能通過已發(fā)布的接口來變更聚合的狀態(tài),并且聚合可以確保一致性,而且不變量可以始終保持良好狀態(tài)。

    在形式上,聚合是關(guān)聯(lián)對象的集群,被視為數(shù)據(jù)變更的單元。外部引用僅限于指定聚合的一個成員,即聚合根。在聚合的邊界內(nèi)需應(yīng)用一組一致性規(guī)則。

    圖 3:定價上下文中的微服務(wù)

    同樣,沒有必要將每個聚合都建模為一個不同的微服務(wù)。事實證明,圖 3 中的服務(wù)(聚合)就是如此,但這不一定是一個規(guī)則。在某些情況下,在單個服務(wù)中托管多個聚合可能是有意義的,特別是在我們不完全了解業(yè)務(wù)領(lǐng)域的情況下。需要注意的一點是,一致性只在單個聚合中才能得到保證,并且聚合只能通過已發(fā)布的接口進行修改。任何違反這些規(guī)則的行為都有增加應(yīng)用程序變成一個大泥球的風(fēng)險。

    上下文映射:一種精確劃分微服務(wù)邊界的方法

    另一個基本工具是上下文映射,同樣,它也是來自領(lǐng)域驅(qū)動設(shè)計。一個單體應(yīng)用通常由不同的模型組成,這些模型大多是緊密耦合的,模型之間可能知道彼此的實現(xiàn)細節(jié),變更一個模型可能造成另一個模型的副作用等等。當你分解單體應(yīng)用時,確定這些模型(在這里是聚合)及其關(guān)系是至關(guān)重要的。上下文映射可以幫助我們做到這一點。它們用于識別和定義各種界限上下文和聚合之間的關(guān)系。在上面的例子中,界限上下文定義了模型的邊界(價格、折扣等等)。而上下文映射定義了這些模型之間以及不同上下文之間的關(guān)系。在確定了這些依賴關(guān)系之后,我們就可以確定下來實現(xiàn)這些服務(wù)的團隊之間的正確協(xié)作模型了。

    對上下文映射的完整探索不在本文的討論范圍之內(nèi),但我們將用一個示例來說明。下圖顯示了處理電子商務(wù)訂單支付的各種應(yīng)用程序。

    購物車上下文負責(zé)訂單的在線授權(quán);訂單上下文處理訂單履行完成后的支付流程,如結(jié)算;聯(lián)絡(luò)中心處理任何異常情況,如支付重試和變更訂單使用的支付方式。為了簡單起見,我們假設(shè)所有這些上下文都是作為單獨的服務(wù)實現(xiàn)的,所有這些上下文封裝了同一個模型。請注意,這些模型在邏輯上是相同的。也就是說,它們都遵循相同的統(tǒng)一領(lǐng)域語言——支付方式、授權(quán)和結(jié)算。只是它們是不同上下文的一部分。

    另一個跡象表明,同一個模型在不同的上下文中傳播,所有這些模型都直接與單個支付網(wǎng)關(guān)相集成,并且彼此執(zhí)行相同的操作。

    圖 4:定義錯誤的上下文映射

    重新定義服務(wù)邊界:將聚合映射到正確的上下文

    在上面的設(shè)計中有一些非常明顯的問題(圖 4)。支付聚合是多個上下文的一部分。在各種服務(wù)之間強制執(zhí)行不變性和一致性是不可能的,更不用說這些服務(wù)之間的并發(fā)問題了。例如,如果在訂單服務(wù)嘗試按之前提交的付款方式進行結(jié)算的過程中,聯(lián)絡(luò)中心更改了與訂單關(guān)聯(lián)的付款方式會發(fā)生什么情況。另外,請注意,支付網(wǎng)關(guān)中的任何更改都將迫使對多個服務(wù)進行更改,可能會涉及到多個團隊,因為它們共同擁有這些上下文。

    通過一些調(diào)整并將聚合與正確的上下文對齊,我們就可以更好地表示這些子域了(圖 5)。需要進行很多的更改。

    我們來看下更改的點:

  • 支付聚合有了一個新家——支付服務(wù)。該服務(wù)還從其他需要支付服務(wù)的服務(wù)中提取了支付網(wǎng)關(guān)。由于單個界限上下文現(xiàn)在擁有了單個聚合,所以不變量很容易管理;所有事務(wù)都在同一個服務(wù)的邊界內(nèi)進行,從而避免了任何并發(fā)問題。

  • 支付聚合使用了反腐層(ACL)將核心領(lǐng)域模型與支付網(wǎng)關(guān)的數(shù)據(jù)模型隔離開來,后者通常是由第三方提供的,可能會發(fā)生變化。在以后的文章中,我們將深入研究基于“端口和適配器”模式的應(yīng)用程序設(shè)計。ACL 層通常包含將支付網(wǎng)關(guān)的數(shù)據(jù)模型轉(zhuǎn)換為支付聚合數(shù)據(jù)模型的適配器。

  • 購物車服務(wù)通過直接調(diào)用 API 的方式來調(diào)用支付服務(wù),因為當客戶在網(wǎng)站上購物時,購物車服務(wù)需要完成支付授權(quán)。

  • 記錄訂單和支付服務(wù)之間的交互作用。訂單服務(wù)發(fā)出一個域事件(稍后會在本文中對此進行詳細介紹)。支付服務(wù)監(jiān)聽此事件并完成訂單的結(jié)算

  • 聯(lián)絡(luò)中心服務(wù)可能有許多聚合,但我們只對該用例中的訂單聚合感興趣。當更改付款方式時,此服務(wù)發(fā)出一個事件,支付服務(wù)將通過以下方式對此事件做出響應(yīng):將先前使用的信用卡撤銷,再處理新的信用卡。

  • 圖 5:重新定義的上下文映射

    通常,單體或遺留應(yīng)用程序有許多聚合,且邊界重疊。創(chuàng)建這些聚合及其依賴關(guān)系的上下文映射,將有助于我們理解從這些單體應(yīng)用中獲取任何新微服務(wù)的輪廓。請記住,微服務(wù)架構(gòu)的成敗取決于聚合之間的低耦合以及聚合之內(nèi)的高內(nèi)聚。

    還需要注意的是,界限上下文本身就是合適的內(nèi)聚單元。即使上下文有多個聚合,也可以將整個上下文及其聚合組成單個微服務(wù)。我們發(fā)現(xiàn)這種啟發(fā)式方法對于有些模糊的領(lǐng)域特別有用,比如組織正在涉足的新業(yè)務(wù)領(lǐng)域。我們可能對分離的正確邊界沒有足夠的了解,并且任何過早的聚合分解都可能導(dǎo)致昂貴的重構(gòu)。試想一下,由于數(shù)據(jù)遷移,不得不將兩個數(shù)據(jù)庫合并為一個,因為我們偶然發(fā)現(xiàn)兩個聚合屬于同一類。但是要確保這些聚合通過接口是充分隔離的,這樣它們就不知道彼此的復(fù)雜細節(jié)了。

    事件風(fēng)暴:另一種識別服務(wù)邊界的技術(shù)

    事件風(fēng)暴(Event Storming)是識別系統(tǒng)中聚合(以及微服務(wù))的另一種必不可少的技術(shù)。它是一個非常有用的工具,既可用于分解單體應(yīng)用,也可用于設(shè)計復(fù)雜的微服務(wù)生態(tài)系統(tǒng)。我們已經(jīng)使用這種技術(shù)分解了一個復(fù)雜的應(yīng)用程序,并打算寫一篇單獨的文章來介紹我們的事件風(fēng)暴經(jīng)驗。在本文中,我們只給出一個快速的高層概述。

    簡而言之,事件風(fēng)暴是在應(yīng)用程序團隊(這里,指單體)中進行的頭腦風(fēng)暴,以識別系統(tǒng)中發(fā)生的各種領(lǐng)域事件和流程。團隊還需確定這些事件影響的總和或模型,以及由此產(chǎn)生的任何后續(xù)影響。當團隊做這個頭腦風(fēng)暴時,他們將識別不同的重疊概念、模棱兩可的領(lǐng)域語言和相互沖突的業(yè)務(wù)流程。他們對相關(guān)的模型進行分組,重新定義聚合并識別重復(fù)的流程。隨著這些工作的進行,這些聚合所屬的界限上下文變得清晰起來。如果所有團隊都在同一個房間(物理或虛擬)里,并開始在 Scrum 風(fēng)格的白板上繪制事件、命令和流程的映射,那么事件風(fēng)暴研討就會非常有用。在本練習(xí)結(jié)束時,通常會產(chǎn)出如下成果:

  • 重新定義的聚合列表。這些可能會成為新的微服務(wù)

  • 需要在這些微服務(wù)之間流動的領(lǐng)域事件

  • 其他應(yīng)用程序或用戶直接調(diào)用的命令

  • 下面是我們在一次事件風(fēng)暴研討會結(jié)束時產(chǎn)生的一個示例樣板。對于團隊來說,就正確的聚合和界限上下文達成一致是一次很棒的協(xié)作活動。此外,團隊在本次會議結(jié)束時還對領(lǐng)域、統(tǒng)一語言和精確的服務(wù)邊界有著共同的理解。

    圖 6:事件風(fēng)暴板

    微服務(wù)之間的通信

    快速回顧一下,一個單體應(yīng)用在單個流程邊界內(nèi)擁有多個聚合。因此,可以在這個邊界內(nèi)管理各個聚合的一致性。例如,如果客戶下了訂單,我們可以減少商品庫存,并向客戶發(fā)送電子郵件,所有這些都在一個事務(wù)中完成。所有操作要么都成功,要么都會失敗。但是,當我們分解了單體并將聚合分散到不同的上下文中時,我們將擁有數(shù)十個甚至數(shù)百個微服務(wù)。但目前為止,存在于單體應(yīng)用單一邊界內(nèi)的流程,現(xiàn)在被分散到了多個分布式系統(tǒng)中。要在所有這些分布式系統(tǒng)中實現(xiàn)事務(wù)的完整性和一致性是非常困難的,而且要以系統(tǒng)的可用性為代價。

    微服務(wù)也是分布式系統(tǒng)。因此,CAP 定理也適用于它們:“一個分布式系統(tǒng)只能提供三個所需特性中的兩個:一致性、可用性和分區(qū)容錯(CAP 中的‘C’——Consistency、‘A’——Availability 和 ‘P’——Partition Tolerance)。”在現(xiàn)實世界的系統(tǒng)中,分區(qū)容錯是不可協(xié)商的——網(wǎng)絡(luò)是不可靠的、虛擬機可以宕機、區(qū)域之間的延遲可能會變得更糟等等。

    因此,我們可以選擇“可用性”或“一致性”。現(xiàn)在,我們知道,在任何現(xiàn)代應(yīng)用程序中,犧牲“可用性”也不是一個好主意。

    圖 7:CAP 定理

    圍繞最終一致性設(shè)計應(yīng)用程序

    如果我們想要跨多個分布式系統(tǒng)構(gòu)建事務(wù),那么我們將再次陷入單體應(yīng)用的困境。但這一次它會是最糟糕的一種單體:一個分散的單體應(yīng)用。如果這些系統(tǒng)中的任何一個變得不可用,則整個流程不可用,通常會導(dǎo)致極差的客戶體驗、承諾的失敗等等。此外,對一個服務(wù)的變更通常會導(dǎo)致另一個服務(wù)的變更,從而引起復(fù)雜和昂貴的部署。因此,我們最好根據(jù)自己的用例來設(shè)計應(yīng)用程序,容忍稍微的不一致,以提高可用性。對于上面的例子,我們可以使所有流程異步,從而達到最終的一致性。我們可以獨立于其他流程,異步發(fā)送電子郵件;如果已經(jīng)承諾的商品以后在倉庫中不可用,那么該商品可能需要補貨,或者我們可以停止接受超出某個閾值的該商品的訂單。

    有時,我們可能會遇到這樣的場景:可能需要跨越不同流程邊界的兩個聚合的強 ACID 式的事務(wù)。這是一個重新審視這些聚合并將它們組合成一個聚合的極好跡象。開始在不同流程邊界中分解這些聚合之前,事件風(fēng)暴和上下文映射將有助于我們及早識別這些依賴關(guān)系。將兩個微服務(wù)合并為一個的成本很高,這是我們應(yīng)該努力避免的。

    支持事件驅(qū)動的架構(gòu)

    微服務(wù)可以將發(fā)生在其聚合上的基本更改發(fā)出來,這些稱為領(lǐng)域事件(Domain Event),并且任何對這些更改感興趣的服務(wù)都可以監(jiān)聽這些事件并在其領(lǐng)域內(nèi)執(zhí)行相應(yīng)的操作。這種方法避免了任何行為上的耦合(一個領(lǐng)域無需規(guī)定其他領(lǐng)域應(yīng)該做什么)以及時間上的耦合(一個流程的成功完成不依賴于所有系統(tǒng)同時可用)。當然,這將意味著系統(tǒng)最終是一致的。

    圖 8:事件驅(qū)動架構(gòu)

    在上面的示例中,訂單服務(wù)發(fā)布一個事件:訂單已取消。訂閱了該事件的其他服務(wù)處理各自的領(lǐng)域功能:支付服務(wù)退款,庫存服務(wù)調(diào)整商品的庫存,等等。為確保此集成的可靠性和彈性,需要注意以下幾點:

  • 生產(chǎn)者應(yīng)確保至少發(fā)出了一次事件。如果過程中出現(xiàn)失敗,則應(yīng)確保存在回退機制以重新觸發(fā)事件

  • 消費者應(yīng)該確保以冪等的方式消費事件。如果再次發(fā)生同一事件,不會對消費者產(chǎn)生任何副作用。事件也可能不是順序到達的。消費者可以使用時間戳或版本號字段來保證事件的唯一性。

  • 由于某些用例的特性,不一定總是可以使用基于事件的集成。請看一下購物車服務(wù)和支付服務(wù)之間的集成。這是一個同步集成,因此我們需要注意一些事項。這是一個行為耦合的例子——購物車服務(wù)可能會從支付服務(wù)調(diào)用一個 REST API,并指示它授權(quán)訂單的支付,而時間耦合——支付服務(wù)需要對購物車服務(wù)可用,它才能接受訂單。這種耦合降低了這些上下文的自治性,也可能會引入不必要的依賴。有幾種方法可以避免這種耦合,但是如果使用了所有這些選項,我們將失去向客戶提供即時反饋的能力。

  • 將 REST API 轉(zhuǎn)換為基于事件的集成。但是,如果支付服務(wù)僅公開 REST API,則此選項可能不可用

  • 購物車服務(wù)立即接受訂單,并且有一個批處理作業(yè)來接管訂單并調(diào)用支付服務(wù) API

  • 購物車服務(wù)生成一個本地事件,然后調(diào)用支付服務(wù) API

  • 在出現(xiàn)失敗和上游依賴項(支付服務(wù))不可用的情況下,將上述方法與重試相結(jié)合,可以產(chǎn)生更具彈性的設(shè)計。例如,在發(fā)生故障的情況下,可以通過基于事件或批處理的重試來備份購物車和支付服務(wù)之間的同步集成。這種方法會對客戶體驗產(chǎn)生額外的影響:客戶可能輸入了不正確的支付詳細信息,當我們離線處理支付時,無法強制他們在線。或者,收回失敗的支付可能會增加企業(yè)的成本。但在所有可能的情況下,讓購物車服務(wù)對支付服務(wù)的不可用性或故障具有彈性,利大于弊。例如,如果我們無法離線收款,我們可以通知客戶。簡而言之,在用戶體驗、彈性和運營成本之間存在著權(quán)衡,在設(shè)計系統(tǒng)時,明智的做法是充分考慮這些折衷。

    避免為了滿足調(diào)用者的特定數(shù)據(jù)需求而編排服務(wù)

    存在于任何面向服務(wù)架構(gòu)的一個反模式是:服務(wù)迎合調(diào)用者的特定訪問模式。通常,當調(diào)用者團隊與服務(wù)提供者團隊緊密合作時,就會發(fā)生這種情況。如果團隊正在開發(fā)一個單體應(yīng)用程序,它們通常會創(chuàng)建一個跨越不同聚合邊界的 API,從而使這些聚合緊密耦合在一起。我們來看一個例子。比如說 Web 中的訂單詳情頁面,移動應(yīng)用程序需要在單個頁面上顯示訂單詳情和訂單退款處理詳情。在一個單體應(yīng)用程序中,訂單獲取 API(Order-GET-API,假設(shè)它是 REST API)需要同時查詢訂單和退款,合并兩個聚合并向調(diào)用方發(fā)送一個復(fù)合響應(yīng)。由于聚合屬于同一流程邊界,因此可以在沒有太多開銷的情況下實現(xiàn)這一點。調(diào)用者可以在一次會話中獲得所需的所有數(shù)據(jù)。

    如果訂單和退款是不同上下文的一部分,那么數(shù)據(jù)不再出現(xiàn)在單個微服務(wù)或聚合邊界內(nèi)。為調(diào)用者保留相同功能的一個選項是,讓訂單服務(wù)負責(zé)調(diào)用退款服務(wù)并創(chuàng)建一個復(fù)合響應(yīng)。這種方法會引起以下幾個問題:

  • 訂單服務(wù)現(xiàn)在與另一個服務(wù)集成,純粹是為了支持那些需要退款數(shù)據(jù)和訂單數(shù)據(jù)的調(diào)用者。現(xiàn)在訂單服務(wù)的自治性降低了,因為退款聚合的任何更改都會導(dǎo)致訂單聚合的更改。

  • 訂單服務(wù)有另一個集成,因此需要考慮另一個故障點:如果退款服務(wù)出現(xiàn)故障,訂單服務(wù)是否仍可以發(fā)送部分數(shù)據(jù),并且調(diào)用者是否可以優(yōu)雅地處理故障呢?

  • 如果調(diào)用者需要變更,以從退款聚合中獲取更多的數(shù)據(jù),那么現(xiàn)在需要兩個團隊同時進行變更

  • 如果跨平臺都遵循這種模式,則可能會導(dǎo)致各種域服務(wù)之間形成復(fù)雜的依賴關(guān)系網(wǎng),這都是因為這些服務(wù)迎合了調(diào)用者特定的訪問模式。

  • 專門服務(wù)于前端的后端(BFFs)

    一種減輕這種風(fēng)險的方法是讓調(diào)用者團隊管理各種域服務(wù)之間的編排。畢竟,調(diào)用方更了解訪問模式,并且可以完全控制對這些模式的任何更改。這種方法將域服務(wù)從表示層解耦出來,讓它們專注于核心業(yè)務(wù)流程。但是,如果 Web 和移動應(yīng)用程序開始直接調(diào)用不同的服務(wù),而不是從單體中調(diào)用一個復(fù)合 API,這可能會給這些應(yīng)用程序帶來性能開銷——在較低帶寬的網(wǎng)絡(luò)上進行多次調(diào)用,處理和合并來自不同 API 的數(shù)據(jù),等等。

    相反,可以使用另一種稱為用于前端的后端模式(Backend for Front-ends)。在這種設(shè)計模式中,由消費者創(chuàng)建和管理的后端服務(wù),在本例中是 Web 和移動團隊,它負責(zé)對多個域服務(wù)進行集成,純粹是為了向客戶提供前端體驗。Web 和移動團隊現(xiàn)在可以根據(jù)它們所需要的用例來設(shè)計數(shù)據(jù)契約。它們甚至可以使用 GraphQL 而不是 REST API 來靈活地查詢并獲取所需的內(nèi)容。需要注意的是,該服務(wù)是由消費者團隊擁有和維護的,而不是提供域服務(wù)的團隊。前端團隊現(xiàn)在可以根據(jù)它們的需求進行優(yōu)化——移動應(yīng)用程序可以請求更小的有效負載,減少來自移動應(yīng)用程序的會話次數(shù),等等。下面是修訂后的業(yè)務(wù)流程圖。BFF 服務(wù)現(xiàn)在為其用例調(diào)用“訂單”和“退款”域服務(wù)。

    圖 9:用于前端的后端

    盡早構(gòu)建 BFF 服務(wù)也很有用,這樣可以避免從單體系統(tǒng)中分解出過多的服務(wù)。否則,要么域服務(wù)必須支持域間編排,要么 Web 和移動應(yīng)用程序必須直接從前端調(diào)用多個服務(wù)。這兩個選項都會導(dǎo)致性能開銷、一次性工作以及團隊之間缺乏自治。

    相關(guān)閱讀:

    1. ?Eric Evans’ Domain Driven Design

    2.? Vaughn Vernon’s Implementing Domain Driven Design

    3.? Martin Fowler’s article on Microservices

    4.? Sam Newman’s Building Microservices

    5.? Event storming

    7.? Backend for Frontends

    8.? Fallacies of distributed computing

    總結(jié)

    以上是生活随笔為你收集整理的如何基于 DDD 构建微服务?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。