天天看點

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

點選檢視第一章 點選檢視第三章

第2章

服務的拆分政策

本章導讀

  • 了解軟體架構,以及它為什麼如此重要
  • 使用拆分模式中的業務能力模式和子域模式進行單體應用到服務的拆分
  • 使用領域驅動設計中的限界上下文概念來分解資料,并讓服務拆分變得更容易

有時你必須對你想得到的東西充滿敬畏。經過激烈的遊說努力,瑪麗終于說服公司裡的所有人:遷移到微服務架構是一件正确的事情。瑪麗感到興奮和惶恐不安,早上她與架構師會面,讨論從哪裡開始。在讨論過程中,顯而易見的是,微服務架構模式語言的某些方面(如部署和服務發現)是他們之前未曾接觸的領域,但卻相對比較簡單。微服務架構的關鍵挑戰是将應用程式功能分解為服務。是以,架構設計的第一個也是最重要的工作就是服務的定義。當FTGO團隊站在白闆周圍時,他們茫然四顧,感覺無從下手。

在本章中,你将學習如何為應用程式定義微服務架構。我描述了将應用程式分解為服務的政策。你将了解服務是圍繞業務問題而非技術問題進行組織的。我還展示了如何使用來自領域驅動設計(DDD)的思想消除上帝類(God Class),這些類一般是在整個應用程式中使用的全局類,常常導緻互相糾結的依賴性,妨礙了服務的分解。

在本章一開始,我會根據軟體架構的概念來定義微服務架構。之後,我會嘗試從應用程式的需求入手,為應用程式定義微服務架構。我會讨論将應用程式分解為服務的過程中可能遇到的障礙,以及解決它們的政策。但在一切開始之前,我們先來看看架構設計的概念。

2.1 微服務架構到底是什麼

第1章描述了微服務架構的關鍵思想是如何進行功能分解。你可以将應用程式建構為一組服務,而不是開發一個大型的單體應用程式。一方面,将微服務架構描述為一種功能分解是有用的。但另一方面,它留下了幾個未解決的問題,包括:微服務架構如何與更廣泛的軟體架構概念相結合?什麼是服務?服務的規模有多重要?

為了回答這些問題,我們需要退後一步,看看軟體架構的含義。軟體的架構是一種抽象的結構,它由軟體的各個組成部分和這些部分之間的依賴關系構成。正如你将在本節中看到的,軟體的架構是多元的,是以有多種方法可以對其進行描述。架構很重要的原因是它決定了應用程式的品質屬性或能力。傳統上,架構的目标是可擴充性、可靠性和安全性。但是今天,該架構能夠快速安全地傳遞軟體,這一點非常重要。你将了解微服務架構是一種架構風格,可為應用程式提供更高的可維護性、可測試性和可部署性。

我将通過描述軟體架構的概念及其重要性來開始本節。接下來,我将讨論架構風格的概念。然後我将微服務架構定義為特定的架構風格。讓我們從了解軟體架構的概念開始。

2.1.1 軟體架構是什麼,為什麼它如此重要

架構顯然很重要。至少有兩個專門讨論該主題的會議:O扲eilly的軟體架構會議(

https://confe-rences.oreilly.com/software-architecture

)和SATURN會議(

https://resources.sei.cmu.edu/news-events/

events/saturn)。許多開發人員的目标是成為一名架構師。但什麼是架構,為什麼它如此重要?

為了回答這個問題,我首先定義術語軟體架構的含義。之後,我将讨論應用程式的架構是多元的,并使用一組視圖或藍圖進行描述。然後我将強調軟體架構的重要性,因為它對應用程式的品質屬性有顯著的影響。

軟體架構的定義

軟體架構有很多定義。例如,維基百科上列舉了大量的定義(

https://en.wikiquote.org/wiki/Software_architecture

)。

我最喜歡的定義來自卡耐基梅隆大學軟體工程研究所(www.sei.cmu.edu)的Len Bass及其同僚,他們在使軟體架構成為一門學科方面發揮了關鍵作用。他們定義的軟體架構如下:

計算機系統的軟體架構是建構這個系統所需要的一組結構,包括軟體元素、它們之間的關系以及兩者的屬性。

—Bass等著《Documenting Software Architectures: Views and Beyond》

這顯然是一個非常抽象的定義。但其實質是應用程式的架構是将軟體分解為元素(element)和這些元素之間的關系(relation)。由于以下兩個原因,分解很重要:

  • 它促進了勞動和知識的分工。它使具有特定專業知識的人們(或多個團隊)能夠就應用程式高效地協同工作。
  • 它定義了軟體元素的互動方式。

将軟體分解成元素以及定義這些元素之間的關系,決定了軟體的能力。

軟體架構的4+1視圖模型

從更具體的角度而言,應用程式的架構可以從多個視角來看,就像建築架構,一般有結構、管線、電氣等多個架構視角。Phillip Krutchen在他經典的論文《Architectural Blueprints —

The 4+1 View Model of Software Architecture》中提出了軟體架構的4+1視圖(www.cs.ubc.ca/~gregor/teaching/papers/4+1view-architecture.pdf)。

圖2-1展示的這套視圖定義了四個不同的軟體架構視圖,每一個視圖都隻描述架構的一個特定方面。每個視圖包括一些特定的軟體元素和它們互相之間的關系。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

每個視圖的目的如下:

  • 邏輯視圖:開發人員建立的軟體元素。在面向對象的語言中,這些元素是類和包。它們之間的關系是類和包之間的關系,包括繼承、關聯和依賴。
  • 實作視圖:建構編譯系統的輸出。此視圖由表示打包代碼的子產品群組件組成,元件是由一個或多個子產品組成的可執行或可部署單元。在Java中,子產品是JAR檔案,元件通常是WAR檔案或可執行JAR檔案。它們之間的關系包括子產品之間的依賴關系以及元件和子產品之間的組合關系。
  • 程序視圖:運作時的元件。每個元素都是一個程序,程序之間的關系代表程序間通信。
  • 部署視圖:程序如何映射到機器。此視圖中的元素由(實體或虛拟)計算機和程序組成。機器之間的關系代表網絡。該視圖還描述了程序和機器之間的關系。

除了這四個視圖以外,4+1中的+1是指場景,它負責把視圖串聯在一起。每個場景負責描述在一個視圖中的多個架構元素如何協作,以完成一個請求。例如,在邏輯視圖中的場景,展現了類是如何協作的。同樣,在程序視圖中的場景,展現了程序是如何協作的。

4+1視圖是描述應用程式架構的絕佳方式。每一個視圖都描述了架構的一個重要側面。場景把視圖中的元素如何協作串聯在一起。現在我們來看看為什麼架構是如此重要。

為什麼架構如此重要

應用程式有兩個層面的需求。第一類是功能性需求,這些需求決定一個應用程式做什麼。這些通常都包含在用例(use case)或者使用者故事(user story)中。應用的架構其實跟這些功能性需求沒什麼關系。功能性需求可以通過任意的架構來實作,甚至是非常糟糕的大泥球架構。

架構的重要性在于,它幫助應用程式滿足了第二類需求:非功能性需求。我們把這類需求也稱之為品質屬性需求,或者簡稱為“能力”。這些非功能性需求決定一個應用程式在運作時的品質,比如可擴充性和可靠性。它們也決定了開發階段的品質,包括可維護性、可測試性、可擴充性和可部署性。為應用程式所選擇的架構将決定這些品質屬性。

2.1.2 什麼是架構的風格

在實體世界中,建築物的建築通常遵循特定的風格,例如維多利亞式、美國工匠式或裝飾藝術式。每種風格都是一系列設計決策,限制了建築的特征和建築材料。建築風格的概念也适用于軟體。David Garlan和Mary Shaw(An Introduction to Software Architecture,January 1994)這兩位軟體架構學科的先驅定義了如下架構風格(

https://www.cs.cmu.edu/afs/cs/project/able/ftp/intro_softarch/intro_softarch.pdf

) :

是以,架構風格根據結構組織模式定義了一系列此類系統。更具體地說,架構風格确定可以在該風格的執行個體中使用的元件和連接配接器的詞彙表,以及關于如何組合它們的一組限制。

特定的架構風格提供了有限的元素(元件)和關系(連接配接器),你可以從中定義應用程式架構的視圖。應用程式通常使用多種架構風格的組合。例如,在本節的後面,我将描述單體架構是如何将實作視圖構造為單個(可執行與可部署)元件的架構樣式。微服務架構将應用程式構造為一組松散耦合的服務。

分層式架構風格

架構的典型例子是分層架構。分層架構将軟體元素按“層”的方式組織。每個層都有明确定義的職責。分層架構還限制了層之間的依賴關系。每一層隻能依賴于緊鄰其下方的層(如果嚴格分層)或其下面的任何層。

可以将分層架構應用于前面讨論的四個視圖中的任何一個。流行的三層架構是應用于邏輯視圖的分層架構。它将應用程式的類組織到以下層中:

  • 表現層:包含實作使用者界面或外部API的代碼。

- 業務邏輯層:包含業務邏輯。

  • 資料持久化層:實作與資料庫互動的邏輯。

分層架構是架構風格的一個很好的例子,但它确實有一些明顯的弊端:

- 單個表現層:它無法展現應用程式可能不僅僅由單個系統調用的事實。

  • 單一資料持久化層:它無法展現應用程式可能與多個資料庫進行互動的事實。
  • 将業務邏輯層定義為依賴于資料持久化層:理論上,這樣的依賴性會妨礙你在沒有資料庫的情況下測試業務邏輯。

此外,分層架構錯誤地表示了精心設計的應用程式中的依賴關系。業務邏輯通常定義資料通路方法的接口或接口庫。資料持久化層則定義了實作存儲庫接口的DAO類。換句話說,依賴關系與分層架構所描述的相反。

讓我們看一下克服這些弊端的替代架構:六邊形架構。

關于架構風格的六邊形

六邊形架構是分層架構風格的替代品。如圖2-2所示,六邊形架構風格選擇以業務邏輯為中心的方式組織邏輯視圖。應用程式具有一個或多個入站擴充卡,而不是表示層,它通過調用業務邏輯來處理來自外部的請求。同樣,應用程式具有一個或多個出站擴充卡,而不是資料持久化層,這些出站擴充卡由業務邏輯調用并調用外部應用程式。此架構的一個關鍵特性和優點是業務邏輯不依賴于擴充卡。相反,各種擴充卡都依賴業務邏輯。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

業務邏輯具有一個或多個端口(port)。端口定義了一組操作,關于業務邏輯如何與外部互動。例如,在Java中,端口通常是Java接口。有兩種端口:入站和出站端口。入站端口是業務邏輯公開的API,它使外部應用程式可以調用它。入站端口的一個執行個體是服務接口,它定義服務的公共方法。出站端口是業務邏輯調用外部系統的方式。出站端口的一個執行個體是存儲庫接口,它定義資料通路操作的集合。

業務邏輯的周圍是擴充卡。與端口一樣,有兩種類型的擴充卡:入站和出站。入站擴充卡通過調用入站端口來處理來自外部世界的請求。入站擴充卡的一個執行個體是Spring MVC Controller,它實作一組REST接口(endpoint)或一組Web頁面。另一個執行個體是訂閱消息的消息代理用戶端。多個入站擴充卡可以調用相同的入站端口。

出站擴充卡實作出站端口,并通過調用外部應用程式或服務處理來自業務邏輯的請求。出站擴充卡的一個執行個體是實作通路資料庫的操作的資料通路對象(DAO)類。另一個執行個體是調用遠端服務的代理類。出站擴充卡也可以釋出事件。

六邊形架構風格的一個重要好處是它将業務邏輯與擴充卡中包含的表示層和資料通路層的邏輯分離開來。業務邏輯不依賴于表示層邏輯或資料通路層邏輯。

由于這種分離,單獨測試業務邏輯要容易得多。另一個好處是它更準确地反映了現代應用程式的架構。可以通過多個擴充卡調用業務邏輯,每個擴充卡實作特定的API或使用者界面。業務邏輯還可以調用多個擴充卡,每個擴充卡調用不同的外部系統。六邊形架構是描述微服務架構中每個服務的架構的好方法。

分層架構和六邊形架構都是架構風格的執行個體。每個都定義了架構的建構塊(元素),并對它們之間的關系施加了限制。六邊形架構和分層架構(三層架構)構成了軟體的邏輯視圖。現在讓我們将微服務架構定義為構成軟體的實作視圖的架構風格。

2.1.3 微服務架構是一種架構風格

前面已經讨論過4+1視圖模型和架構風格,是以現在可以開始定義單體架構和微服務架構。它們都是架構風格。單體架構是一種架構風格,它的實作視圖是單個元件:單個可執行檔案或WAR檔案。這個定義并沒有說明其他的視圖。例如,單體應用程式可以具有六邊形架構風格的邏輯視圖。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

微服務架構也是一種架構風格。它的實作視圖由多個元件構成:一組可執行檔案或WAR檔案。它的元件是服務,連接配接器是使這些服務能夠協作的通信協定。每個服務都有自己的邏輯視圖架構,通常也是六邊形架構。圖2-3顯示了FTGO應用程式可能的微服務架構。此架構中的服務對應于業務功能,例如訂單管理和餐館管理。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

在本章後面,我将描述業務能力(business capability)的含義。服務之間的連接配接器使用程序間通信機制(如REST API和異步消息)實作。第3章将更詳細地讨論程序間通信。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

微服務架構強加的一個關鍵限制是服務松耦合。是以,服務之間的協作方式存在一定限制。為了解釋這些限制,我将嘗試定義什麼是服務,解釋松耦合意味着什麼,并告訴你為什麼這很重要。

什麼是服務

服務是一個單一的、可獨立部署的軟體元件,它實作了一些有用的功能。圖2-4顯示了服務的外部視圖,在此示例中是Order Service。服務具有API,為其用戶端提供對功能的通路。有兩種類型的操作:指令和查詢。API由指令、查詢和事件組成。指令如createOrder()執行操作并更新資料。查詢,如findOrderById()檢索資料。服務還釋出由其用戶端使用的事件,例如OrderCreated。

服務的API封裝了其内部實作。與單體架構不同,開發人員無法繞過服務的API直接通路服務内部的方法或資料。是以,微服務架構強制實作了應用程式的子產品化。

微服務架構中的每項服務都有自己的架構,可能還有獨特的技術棧。但是典型的服務往往都具有六邊形架構。其API由與服務的業務邏輯互動的擴充卡實作。操作擴充卡調用業務邏輯,事件擴充卡對外釋出業務邏輯産生的事件。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

API定義了由用戶端調用的操作。有兩種類型的操作:指令用來更新資料,查詢用來檢索資料。當服務的資料發生更改時,服務會釋出可供用戶端訂閱的事件

在第12章讨論部署技術時,你将看到服務的實作視圖可以采用多種形式。該元件可以是獨立程序,在容器中運作的Web應用程式或OSGI包、雲主機或Serverless技術,等等。但是,一個基本要求是服務具有API并且可以獨立部署。

什麼是松耦合

微服務架構的最核心特性是服務之間的松耦合性(

https://en.wikipedia.org/wiki/Loose_coupling

) 。服務之間的互動采用API完成,這樣做就封裝了服務的實作細節。這允許服務在不影響用戶端的情況下,對實作方式做出修改。松耦合服務是改善開發效率、提升可維護性和可測試性的關鍵。小的、松耦合的服務更容易被了解、修改和測試。

我們通過API來實作松耦合服務之間的協調調用,這樣就避免了外界對服務的資料庫的直接通路和調用。服務自身的持久化資料就如同類的私有屬性一樣,是不對外的。保證資料的私有屬性是實作松耦合的前提之一。這樣做,就允許開發者修改服務的資料結構,而不用提前與其他服務的開發者互相協商。這樣做在運作時也實作了更好的隔離。例如,一個服務的資料庫加鎖不會影響另外的服務。但是你稍後就會看到在服務間不共享資料庫的弊端,特别是處理資料一緻性和跨服務查詢都變得更為複雜。

共享類庫的角色

開發人員經常把一些通用的功能打包到庫或子產品中,以便多個應用程式可以重用它而無須複制代碼。畢竟,如果沒有Maven或npm庫,我們今天的開發工作都會變得更困難。你可能也想在微服務架構中使用共享庫。從表面上看,它似乎是減少服務中代碼重複的好方法。但是你需要確定不會意外地在服務之間引入耦合。

例如,想象一下多個服務需要更新Order業務對象的場景。一種選擇是将該功能打包為可供多個服務使用的庫。一方面,使用庫可以消除代碼重複。另一方面,如果業務需求的變更影響了Order業務對象,開發者需要同時重建和重新部署所有使用了共享庫的服務。更好的選擇是把這些可能會更改的通用功能(例如Order管理)作為服務來實作,而不是共享庫。

你應該努力使用共享庫來實作不太可能改變的功能。例如,在典型的應用程式中,在每個服務中都實作一個通用的Money類(例如用來實作币種轉換等固定功能)沒有任何意義。相反,你應該建立一個供所有服務使用的共享庫。

服務的大小并不重要

微服務這個術語的一個問題是會将你的關注點錯誤地聚焦在微上。它暗示服務應該非常小。其他基于大小的術語(如miniservice或nanoservice)也是如此。實際上,大小不是一個重要的考慮因素。

更好的目标是将精心設計的服務定義為能夠由小團隊開發的服務,并且傳遞時間最短,與其他團隊協作最少。理論上,團隊可能隻負責單一服務,是以服務絕不是微小的。相反,如果服務需要大型團隊或需要很長時間進行測試,那麼拆分團隊或服務可能是有意義的。另外,如果你因為其他服務的變更而不斷需要同步更新自己負責的服務,或者你所負責的服務正在觸發其他服務的同步更新,那麼這表明服務沒有實作松耦合。你建構的甚至可能是一個分布式的單體。

微服務架構把應用程式通過一些小的、松耦合的服務組織在一起。結果,這樣的架構提升了開發階段的效率,特别是可維護性、可測試性和可部署性,這也就讓組織的軟體開發速度更快。微服務架構也同時提升了應用程式的可擴充性,盡管這不是微服務的主要目标。為了使用微服務架構開發軟體,你首先需要識别服務,并确定它們之間如何協作。現在我們來看看如何定義一個應用程式的微服務架構。

2.2 為應用程式定義微服務架構

那麼如何定義一個微服務架構呢?跟所有的軟體開發過程一樣,一開始我們需要拿到領域專家或者現有應用的需求文檔。跟所有的軟體開發一樣,定義架構也是一項藝術而非技術。本節我們将介紹一種定義應用程式架構的三步式流程,如圖2-5所示。世界上并沒有一個機械化的流程可以遵循,然後指望這個流程輸出一個合理的架構。我們隻能介紹一個大概的方法,現實世界中,這是一個不斷疊代和持續創新的過程。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

應用程式是用來處理用戶端請求的,是以定義其架構的第一步是将應用程式的需求提煉為各種關鍵請求。但是,不是根據特定的程序間通信技術(如REST或消息)來描述這些請求,而是使用更抽象的系統操作這個概念。系統操作(system operation)是應用程式必須處理的請求的一種抽象描述。它既可以是更新資料的指令,也可以是檢索資料的查詢。每個指令的行為都是根據抽象領域模型定義的,抽象領域模型也是從需求中派生出來的。系統操作是描述服務之間協作方式的架構場景。

該流程的第二步是确定如何分解服務。有幾種政策可供選擇。一種源于業務架構學派的政策是定義與業務能力相對應的服務。另一種政策是圍繞領域驅動設計的子域來分解和設計服務。但這些政策的最終結果都是圍繞業務概念而非技術概念分解和設計的服務。

定義應用程式架構的第三步是确定每個服務的API。為此,你将第一步中辨別的每個系統操作配置設定給服務。服務可以完全獨立地實作操作。或者,它可能需要與其他服務協作。在這種情況下,你可以确定服務的協作方式,這通常需要服務來支援其他操作。你還需要确定選用第3章中描述的哪種程序間通信機制來實作每個服務的API。

服務的分解有幾個障礙需要克服。首先是網絡延遲。你可能會發現,由于服務之間的網絡往返太多,特定的分解将是不切實際的。分解的另一個障礙是服務之間的同步通信降低了可用性。你可能需要使用第3章中描述的自包含服務的概念。第三個障礙是需要維護跨服務的資料一緻性。你需要使用第4章中讨論的Saga。分解的第四個也是最後一個障礙是所謂的上帝類(God Class),它廣泛應用在整個應用程式中。幸運的是,你可以使用領域驅動設計中的概念來消除上帝類。

本節首先介紹如何識别應用程式的系統操作。之後,會研究将應用程式分解為服務的政策和指南、分解的障礙以及如何解決它們。最後,将描述如何定義每個服務的API。

2.2.1 識别系統操作

定義應用程式架構的第一步是定義系統操作。起點是應用程式的需求,包括使用者故事及其相關的使用者場景(請注意,這些與架構場景不同)。使用圖2-6中所示的兩步式流程識别和定義系統操作。這個流程的靈感來自Craig Larman的名著《Applying UML and Patterns》(Prentice Hall,2004)中介紹的面向對象設計過程(www.craiglarman.com/wiki/index.php?title=Book_Applying_UML_and_Patterns) 。第一步建立由關鍵類組成的抽象領域模型,這些關鍵類提供用于描述系統操作的詞彙表。第二步确定系統操作,并根據領域模型描述每個系統操作的行為。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

第一步是建立一個抽象領域模型。第二步是定義系統操作,這些操作是根據領域模型定義的

領域模型主要源自使用者故事中提及的名詞,系統操作主要來自使用者故事中提及的動詞。你還可以使用名為事件風暴(Event Storming)的技術定義領域模型,我将在第5章中讨論。每個系統操作的行為都是根據它對一個或多個領域對象的影響以及它們之間的關系來描述的。系統操作可以建立、更新或删除領域對象,以及建立或破壞它們之間的關系。

我們來看看如何定義抽象領域模型。之後,我将根據領域模型定義系統操作。

建立抽象領域模型

定義系統操作的第一步是為這個應用程式描繪一個抽象的領域模型。注意這個模型比我們最終要實作的簡單很多。應用程式本身并不需要一個領域模型,因為我們在稍後會學到,每一個服務都有它自己的領域模型。盡管非常簡單,抽象的領域模型仍舊有助于在開始階段提供幫助,因為它定義了描述系統操作行為的一些詞語。

建立領域模型會采用一些标準的技術,例如通過與領域專家溝通後,分析使用者故事和場景中頻繁出現的名詞。例如Place Order使用者故事,我們可以把它分解為多個使用者場景,例如這個:

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

在這個使用者場景中的名詞,如Consumer、Order、Restaurant和CreditCard,暗示了這些類都是需要的。

同樣,Accept Order使用者故事也可以分解為多個場景,如下:

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

這個場景暗示需要Courier類和Delivery類。在經過幾次疊代分析之後,結果顯然就是這個領域模型應該包括一些類,如MenuItem和Address等。圖2-7顯示了核心類的類圖。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

每一個類的作用如下:

  • Consumer:下訂單的使用者。
  • Order:使用者下的訂單,它用來描述訂單并跟蹤狀态。
  • OrderLineItem:Order中的一個條目。
  • DeliveryInfo:送餐的時間和位址。
  • Restaurant:為使用者準備生産訂單的餐館,同時也要發起送貨。
  • MenuItem:餐館菜單上的一個條目。
  • Courier:送餐員負責把訂單送到使用者手裡。可跟蹤送餐員的可用性和他們的位置。
  • Address:Consumer或Restaurant的位址。
  • Location:Courier目前的位置,用經緯度表示。

類似圖2-7這種類圖描述了應用程式架構的一個方面。但如果沒有對應的場景,這個圖也就是僅僅好看而已,并不實用。下一步開始定義對應架構場景的系統操作。

定義系統操作

當定義了抽象的領域模型之後,接下來就要識别系統必須處理的各種請求。我們并不讨論具體的使用者界面,但是你能夠想象在每一個使用者場景下,前端的使用者界面向後端的業務邏輯送出請求,後端的業務邏輯進行資料的擷取和處理。FTGO是一個Web應用,這意味着它的大部分請求都是基于HTTP的。但也有可能一些用戶端會使用消息。相比綁定到具體的通信協定,使用抽象的詞彙來描述跟系統操作有關的請求更為合理。

有以下兩種類型的系統操作。

  • 指令型:建立、更新或删除資料的系統操作。
  • 查詢型:查詢和讀取資料的系統操作。

從根本上說,這些系統操作都會對應到具體的REST、RPC或消息端口。但現階段我們不必在意這些實作細節。讓我們先開始識别一些指令。

識别系統指令的切入點是分析使用者故事和場景中的動詞。例如Place Order使用者故事,它非常明确地告訴架構師,這個系統必須提供一個Create Order操作。很多使用者故事都會直接對應或映射為系統指令。表2-1列出了一些關鍵的系統指令。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

指令規範定義了指令對應的參數、傳回值和領域模型類的行為。行為規範中包括前置條件(即當這個操作被調用時必須滿足的條件)和後置條件(即這個操作被調用後必須滿足的條件)。例如,以下就是createOrder()系統操作的規範。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

前置條件對應着Place Order使用者場景中的givens,後置條件對應着場景中的Then。當系統操作被調用時,它會檢查前置條件,執行操作來完成和滿足後置條件。

下面是acceptOrder()的系統操作規範:

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

前置條件和後置條件對應着之前使用者場景中的描述。

多數與系統操作相關的架構元素是指令。查詢雖然僅僅是簡單地擷取資料,但是也同樣重要。

應用程式除了實作指令以外,也必須實作查詢。查詢為使用者決策提供了使用者界面。在目前階段,我們并沒有開始為FTGO應用程式構思任何使用者界面,但是需要注意,當消費者下訂單時往往是如下所示的過程。

1.使用者輸入送餐位址和期望的送餐時間;

2.系統顯示目前可用的餐館;

3.使用者選擇餐館;

4.系統顯示餐館的菜單;

5.使用者點餐并結賬;

6.系統建立訂單。

這個使用者場景包含了以下的查詢型操作:

  • findAvailableRestaurants(deliveryAddress,deliveryTime):擷取所有能夠送餐到使用者位址并滿足送餐時間要求的餐館。
  • findRestaurantMenu(id):傳回餐館資訊和這家餐館的菜單項。

在這兩項查詢中,findAvailableRestaurants()也許是在架構層面尤其重要的一個。它是一個包含了地理位置等資訊的複雜查詢。地理查詢的元件負責找到送餐位址周圍所有滿足要求的餐館位置。同時它也需要過濾那些在訂單準備和送餐時間範圍内沒有營業的餐館。另外,這個查詢的性能尤其重要,因為執行這個查詢時,客戶多數都是“線上急等”的狀态,耽誤不得。

抽象的領域模型和系統操作能夠回答這個應用“做什麼”這一問題。這有助于推動應用程式的架構設計。每一個系統操作的行為都通過領域模型的方式來描述。每一個重要的系統操作都對應着架構層面的一個重大場景,是架構中需要較長的描述和特别考慮的地方。現在我們來看看如何定義應用程式的微服務架構。

系統操作被定義後,下一步就是完成應用服務的識别。如之前提到的,這并不是一個機械化的流程,相反,有多種拆分政策可供選擇。每一種都是從一個側面來解決問題,并且使用它們獨有的一些術語。但是殊途同歸,這些政策的結果都是一樣的:一個包含若幹服務的架構,這樣的架構是以業務而不是技術概念為中心。

我們先來看看第一個政策:使用業務能力來定義服務。

2.2.2 根據業務能力進行服務拆分

建立微服務架構的政策之一就是采用業務能力進行服務拆分。業務能力是一個來自于業務架構模組化的術語。業務能力是指一些能夠為公司(或組織)産生價值的商業活動。特定業務的業務能力取決于這個業務的類型。例如,保險公司業務能力通常包括承保、理賠管理、賬務和合規等。線上商店的業務能力包括:訂單管理、庫存管理和發貨,等等。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

業務能力定義了一個組織的工作

組織的業務能力通常是指這個組織的業務是做什麼,它們通常都是穩定的。與之相反,組織采用何種方式來實作它的業務能力,是随着時間不斷變化的。這個準則在今天尤其明顯,很多新技術在被快速采用,商業流程的自動化程度越來越高。例如,不久之前你還通過把支票交給銀行櫃員的方式來兌現支票,現在很多ATM機都支援直接兌現支票,而今,人們甚至可以使用智能手機拍照的方式來兌現支票。正如你所見,“兌現支票”這個業務能力是穩定不變的,但是這個能力的實作方式正在發生戲劇性的變化。

識别業務能力

一個組織有哪些業務能力,是通過對組織的目标、結構和商業流程的分析得來的。每一個業務能力都可以被認為是一個服務,除非它是面向業務的而非面向技術的。業務能力規範包含多項元素,比如輸入和輸出、服務等級協定(SLA)。例如,保險承保能力的輸入來自客戶的應用程式,這個業務能力的輸出是完成核保并報價。

業務能力通常集中在特定的業務對象上。例如,理賠業務對象是理賠管理功能的重點。能力通常可以分解為子能力。例如,理賠管理能力具有多個子能力,包括理賠資訊管理、理賠稽核和理賠付款管理。

把FTGO的業務能力逐一列出來似乎也并不太困難,如下所示。

  • 供應商管理。
    • Courier management:送餐員相關資訊管理;
    • Restaurant information management:餐館菜單和其他資訊管理,例如營業位址和時間。
  • 消費者管理:消費者有關資訊的管理。
  • 訂單擷取和履行。
    • Order management:讓消費者可以建立和管理訂單。
    • Restaurant order management:讓餐館可以管理訂單的生産過程。

      送餐。

    • Courier availability management:管理送餐員的實時狀态。
    • Delivery management:把訂單送到使用者手中。
  • 會計記賬。
    • Consumer accounting:管理跟消費者相關的會計記賬。
    • Restaurant accounting:管理跟餐館相關的會計記賬。
    • Courier accounting:管理跟送餐員相關的會計記賬。
  • 其他。

頂級能力包括供應商管理、消費者管理、訂單擷取和履行以及會計記賬。可能還有許多其他頂級能力,包括與營銷相關的能力。大多數頂級能力都會分解為子能力。例如,訂單擷取和履行被分解為五個子能力。

這個能力層次的有趣方面是有三個餐館相關的能力:餐館資訊管理、餐館訂單管理和餐館會計記賬。那是因為它們代表了餐館營運的三個截然不同的方面。

接下來,我們将了解如何使用業務能力來定義服務。

從業務能力到服務

一旦确定了業務能力,就可以為每個能力或相關能力組定義服務。圖2-8顯示了FTGO應用程式從能力到服務的映射。某些頂級能力(如會計記賬能力)将映射到服務。在其他情況下,子能力映射到服務。

決定将哪個級别的能力層次結構映射到服務是一個非常主觀的判斷。我對這種特定映射的理由如下:

  • 我将供應商管理的子能力映射到兩種服務,因為餐館和送餐員是非常不同類型的供應商。
  • 我将訂單擷取和履行能力映射到三個服務,每個服務負責流程的不同階段。我将送餐員可用性管理(Courier availability management)和傳遞管理(Delivery management)能力結合起來,并将它們映射到單個服務,因為它們交織在一起。
  • 我将會計記賬能力映射到自己的獨立服務,因為不同類型的會計記賬看起來很相似。
    帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

之後将針對餐館和送餐員的費用支付和針對消費者的訂單收款分開是有意義的。

圍繞能力組織服務的一個關鍵好處是,因為它們是穩定的,是以最終的架構也将相對穩定。架構的各個元件可能會随着業務的具體實作方式的變化而發展,但架構仍保持不變。

話雖如此,重要的是要記住圖2-8中顯示的服務僅僅是定義架構的第一次嘗試。随着我們對應用程式領域的了解越來越多,它們可能會随着時間的推移而變化,特别是架構定義流程中的一個重要步驟是調查服務如何在每個關鍵架構服務中協作。例如,你可能會發現由于過多的程序間通信而導緻特定的分解效率低下,導緻你必須把一些服務組合在一起。相反,服務可能會在複雜性方面增長到值得将其拆分為多個服務的程度。此外,在2.2.5節中将描述可能導緻你重新審視目前分解決策的幾個障礙。

現在讓我們看看基于領域驅動設計分解應用程式的方法。

2.2.3 根據子域進行服務拆分

Eric Evans在他的經典著作中(Addison-Wesley Professional,2003)提出的領域驅動設計是建構複雜軟體的方法論,這些軟體通常都以面向對象和領域模型為核心。領域模型以解決具體問題的方式包含了一個領域内的知識。它定義了目前領域相關團隊的詞彙表,DDD也稱之為通用語言(Ubiquitous language)。領域模型會被緊密地映射到應用的設計和實作環節。在微服務架構的設計層面,DDD有兩個特别重要的概念,子域和限界上下文。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

傳統的企業架構模組化方式往往會為整個企業建立一個單獨的模型,DDD則采取了完全不同的方式。在這樣的模型中,會有适用于整個應用全局的業務實體定義,例如客戶或訂單。這類傳統模組化方式的挑戰在于,讓組織内的所有團隊都對全局單一的模組化和術語定義達成一緻是非常困難的。另外,對于組織中的特定團隊而言,這個單一的業務實體定義可能過于複雜,超出了他們的需求。此外,這些傳統的領域模型可能會造成混亂,因為組織内有些團隊可能針對不同的概念使用相同的術語,而也有些團隊會針對同一個概念使用不同的術語。DDD通過定義多個領域模型來避免這個問題,每個領域模型都有明确的範圍。

領域驅動為每一個子域定義單獨的領域模型。子域是領域的一部分,領域是DDD中用來描述應用程式問題域的一個術語。識别子域的方式跟識别業務能力一樣:分析業務并識别業務的不同專業領域,分析産出的子域定義結果也會跟業務能力非常接近。FTGO的子域包括:訂單擷取、訂單管理、餐館管理、送餐和會計。正如你所見:這些子域跟我們之前定義的業務能力非常接近。

DDD把領域模型的邊界稱為限界上下文(bounded context)。限界上下文包括實作這個模型的代碼集合。當使用微服務架構時,每一個限界上下文對應一個或者一組服務。換一種說法,我們可以通過DDD的方式定義子域,并把子域對應為每一個服務,這樣就完成了微服務架構的設計工作。圖2-9展示了子域和服務之間的映射,每一個子域都有屬于它們自己的領域模型。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

DDD和微服務架構簡直就是天生一對。DDD的子域和限界上下文的概念,可以很好地跟微服務架構中的服務進行比對。而且,微服務架構中的自治化團隊負責服務開發的概念,也跟DDD中每個領域模型都由一個獨立團隊負責開發的概念吻合。更有趣的是,子域用于它自己的領域模型這個概念,為消除上帝類和優化服務拆分提供了好辦法。

按子域分解和按業務能力分解是定義應用程式的微服務架構的兩種主要模式。但是,也有一些有用的拆分指導原則源于面向對象的設計。我們來詳細讨論這些原則。

2.2.4 拆分的指導原則

到目前為止,在本章中,我們已經了解了定義微服務架構的主要方法。在應用微服務架構模式時,我們還可以采納和使用面向對象設計中的一些原則。面向對象設計的一些原則也可以用于指導微服務架構的設計工作。這些原則由Robert C. Martin在他的著作《Designing Object Oriented C++ Applications Using The Booch Method》(Prentice Hall,1995)中提出。第一個原則就是在定義類的職責時,應該遵循單一職責原則(Single Responsibility Principle,SRP)。第二個原則是把類組成包時,應該遵循閉包原則(Common Closure Principle,CCP)。讓我們來看看這些原則如何應用到微服務架構。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

類所承載的每一個職責都是對它進行修改的潛在原因。如果一個類承載了多個職責,并且互相之間的修改是獨立的,那麼這個類就會變得非常不穩定。遵照SRP原則,你所定義的每一個類都應該隻有一個職責,是以也就隻有一個理由對它進行修改。

我們在設計微服務架構時應該遵循SRP原則,設計小的、内聚的、僅僅含有單一職責的服務。這會縮小服務的大小并提升它的穩定性。新的FTGO架構是應用SRP的一個例子。為客戶擷取餐食的每一個方面(訂單擷取、訂單準備、送餐等)都由一個單一的服務承載。

閉包原則

另外一個有用的原則是閉包原則(CCP):

在包中包含的所有類應該是對同類的變化的一個集合,也就是說,如果對包做出修改,需要調整的類應該都在這個包之内。

—Robert C. Martin

這就意味着,如果由于某些原因,兩個類的修改必須耦合先後發生,那麼就應該把它們放在同一個包内。也許,這些類實作了一些特定的業務規則的不同方面。這樣做的目标是當業務規則發生變化時,開發者隻需要對一個傳遞包做出修改,而不是大規模地修改(和重新編譯)整個應用。采用閉包原則,極大地改善了應用程式的可維護性。

在微服務架構下采用CCP原則,這樣我們就能把根據同樣原因進行變化的服務放在一個元件内。這樣做可以控制服務的數量,當需求發生變化時,變更和部署也更加容易。理想情況下,一個變更隻會影響一個團隊和一個服務。CCP是解決分布式單體這種可怕的反模式的法寶。

單一職責原則和閉包原則是Bob Martin制定的十一項原則中的兩項。它們在開發微服務架構時特别有用。在設計類和包時可以使用其餘的九個原則。有關單一職責原則、閉包原則和其他面向對象設計原則的更多資訊,請參閱Bob Martin網站上的文章《面向對象設計的原則》(

http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

按業務能力和子域以及單一職責原則和閉包原則進行分解是将應用程式分解為服務的好方法。為了應用它們并成功開發微服務架構,你還必須解決一些事務管理和程序間通信問題。

2.2.5 拆分單體應用為服務的難點

從表面上看,通過定義與業務能力或子域相對應的服務來建立微服務架構的政策看起來很簡單。但是,你可能會遇到幾個障礙:

  • 網絡延遲。
  • 同步程序間通信導緻可用性降低。
  • 在服務之間維持資料一緻性。
  • 擷取一緻的資料視圖。
  • 上帝類阻礙了拆分。

讓我們來看看每個問題,先從網絡延遲開始。

網絡延遲

網絡延遲是分布式系統中一直存在的問題。你可能會發現,對服務的特定分解會導緻兩個服務之間的大量往返調用。有時,你可以通過實施批處理API在一次往返中擷取多個對象,進而将延遲減少到可接受的數量。但在其他情況下,解決方案是把多個相關的服務組合在一起,用程式設計語言的函數調用替換昂貴的程序間通信。

同步程序間通信導緻可用性降低

另一個需要考慮的問題是如何處理程序間通信而不降低系統的可用性。例如,實作createOrder()操作最常見的方式是讓Order Service使用REST同步調用其他服務。這樣做的弊端是REST這樣的協定會降低Order Service的可用性。如果任何一個被調用的服務處在不可用的狀态,那麼訂單就無法建立了。有時候這可能是一個不得已的折中,但是在第3章中學習異步消息之後,你就會發現其實有更好的辦法來消除這類同步調用産生的緊耦合并提升可用性。

在服務之間維持資料一緻性

另一個挑戰是如何在某些系統操作需要更新多個服務中的資料時,仍舊維護服務之間的資料一緻性。例如,當餐館接受訂單時,必須在Kitchen Service和Delivery Service中同時進行更新。Kitchen Service會更改Ticket的狀态。Delivery Service安排訂單的傳遞。這些更新都必須以原子化的方式完成。

傳統的解決方案是使用基于兩階段送出(two phase commit)的分布式事務管理機制。但正如你将在第4章中看到的那樣,對于現今的應用程式而言,這不是一個好的選擇,你必須使用一種非常不同的方法來處理事務管理,這就是Saga。Saga是一系列使用消息協作的本地事務。Saga比傳統的ACID事務更複雜,但它們在許多情況下都能工作得很好。Saga的一個限制是它們最終是一緻的。如果你需要以原子方式更新某些資料,那麼它必須位于單個服務中,這可能是分解的障礙。

擷取一緻的資料視圖

分解的另一個障礙是無法跨多個資料庫獲得真正一緻的資料視圖。在單體應用程式中,ACID事務的屬性保證查詢将傳回資料庫的一緻視圖。相反,在微服務架構中,即使每個服務的資料庫是一緻的,你也無法獲得全局一緻的資料視圖。如果你需要一些資料的一緻視圖,那麼它必須駐留在單個服務中,這也是服務分解所面臨的問題。幸運的是,在實踐中這很少帶來真正的問題。

上帝類阻礙了拆分

分解的另一個障礙是存在所謂的上帝類。上帝類是在整個應用程式中使用的全局類(

http://wiki.c2.com/?GodClass

) 。上帝類通常為應用程式的許多不同方面實作業務邏輯。它有大量字段映射到具有許多列的資料庫表。大多數應用程式至少有一個這樣的上帝類,每個類代表一個對領域至關重要的概念:銀行賬戶、電子商務訂單、保險政策,等等。因為上帝類将應用程式的許多不同方面的狀态和行為捆綁在一起,是以将使用它的任何業務邏輯拆分為服務往往都是一個不可逾越的障礙。

Order類是FTGO應用程式中上帝類的一個很好的例子。這并不奇怪:畢竟FTGO的目的是向客戶提供食品訂單。系統的大多數部分都涉及訂單。如果FTGO應用程式具有單個領域模型,則Order類将是一個非常大的類。它将具有與應用程式的許多不同部分相對應的狀态和行為。圖2-10顯示了使用傳統模組化技術建立的Order類的結構。

如你所見,Order類具有與訂單處理、餐館訂單管理、送餐和付款相對應的字段及方法。由于一個模型必須描述來自應用程式的不同部分的狀态轉換,是以該類還具有複雜的狀态模型。在目前情況下,這個類的存在使得将代碼分割成服務變得極其困難。

一種解決方案是将Order類打包到庫中并建立一個中央Order資料庫。處理訂單的所有服務都使用此庫并通路通路資料庫。這種方法的問題在于它違反了微服務架構的一個關鍵原則,并導緻我們特别不願意看到的緊耦合。例如,對Order模式的任何更改都要求其他開發團隊同步更新和重新編譯他們的代碼。

另一種解決方案是将Order資料庫封裝在Order Service中,該服務由其他服務調用以檢索和更新訂單。該設計的問題在于這樣的一個Order Service将成為一個純資料服務,成為包含很少或沒有業務邏輯的貧血領域模型(anemic domain model)。這兩種解決方案都沒有吸引力,但幸運的是,DDD提供了一個好的解決方案。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

更好的方法是應用DDD并将每個服務視為具有自己的領域模型的單獨子域。這意味着FTGO應用程式中與訂單有關的每個服務都有自己的領域模型及其對應的Order類的版本。Delivery Service是多領域模型的一個很好的例子。如圖2-11所示為Order,它非常簡單:取餐位址、取餐時間、送餐位址和送餐時間。此外,Delivery Service使用更合适的Delivery名稱,而不是稱之為Order。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

Delivery Service對訂單的任何其他屬性不感興趣。

Kitchen Service有一個更簡單的訂單視圖。它的Order版本就是一個Ticket(後廚工單)。如圖2-12所示,Ticket隻包含status、requestedDeliveryTime、prepareByTime以及告訴餐館準備的訂單項清單。它不關心消費者、付款、傳遞等這些與它無關的事情。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

Order Service具有最複雜的訂單視圖,如圖2-13所示。即使它有相當多的字段和方法,它仍然比原始版本的那個Order上帝類簡單得多。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

每個領域模型中的Order類表示同一Order業務實體的不同方面。FTGO應用程式必須維持不同服務中這些不同對象之間的一緻性。例如,一旦Order Service授權消費者的信用卡,它必須觸發在Kitchen Service中建立Ticket。同樣,如果Kitchen Service拒絕訂單,則必須在Order Service中取消訂單,并且為客戶退款。在第4章中,我們将學習如何使用前面提到的事件驅動機制Saga來維護服務之間的一緻性。

除了造成一些技術挑戰以外,擁有多個領域模型還會影響使用者體驗。應用程式必須在使用者體驗(即其自己的領域模型)與每個服務的領域模型之間進行轉換。例如,在FTGO應用程式中,向消費者顯示的Order狀态來自存儲在多個服務中的Order資訊。這種轉換通常由API Gateway處理,将在第8章中讨論。盡管存在這些挑戰,但在定義微服務架構時,必須識别并消除上帝類。

我們現在來看看如何定義服務API。

2.2.6 定義服務API

到目前為止,我們有一個系統操作清單和一個潛在服務清單。下一步是定義每個服務的API:也就是服務的操作和事件。存在服務API操作有以下兩個原因:首先,某些操作對應于系統操作。它們由外部用戶端調用,也可能由其他服務調用。另次,存在一些其他操作用以支援服務之間的協作。這些操作僅由其他服務調用。

服務通過對外釋出事件,使其能夠與其他服務協作。第4章将描述如何使用事件來實作Saga,這些Saga可以維護服務之間的資料一緻性。第7章将讨論如何使用事件來更新CQRS視圖,這些視圖支援有效的查詢。應用程式還可以使用事件來通知外部用戶端。例如,可以使用WebSockets将事件傳遞給浏覽器。

定義服務API的起點是将每個系統操作映射到服務。之後确定服務是否需要與其他服務協作以實作系統操作。如果需要協作,我們将确定其他服務必須提供哪些API才能支援協作。首先來看一下如何将系統操作配置設定給服務。

把系統操作配置設定給服務

第一步是确定哪個服務是請求的初始入口點。許多系統操作可以清晰地映射到服務,但有時映射會不太明顯。例如,考慮使用noteUpdatedLocation()操作來更新送餐員的位置。一方面,因為它與送餐員有關,是以應該将此操作配置設定給Courier Service。另一方面,它是需要送餐地點的Delivery Service。在這種情況下,将操作配置設定給需要操作所提供資訊的服務是更好的選擇。在其他情況下,将操作配置設定給具有處理它所需資訊的服務可能是有意義的。

表2-2顯示了FTGO應用程式中的哪些服務負責哪些操作。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

把操作配置設定給服務後,下一步是确定在處理每一個系統操作時,服務之間如何互動。

确定支援服務協作所需要的API

某些系統操作完全由單個服務處理。例如,在FTGO應用程式中,Consumer Service完全獨立地處理createConsumer()操作。但是其他系統操作跨越多個服務。處理這些請求之一所需的資料可能分散在多個服務周圍。例如,為了實作createOrder()操作,Order Service必須調用以下服務以驗證其前置條件并使後置條件成立:

  • Consumer Service:驗證消費者是否可以下訂單并擷取其付款資訊。
  • Restaurant Service:驗證訂單行項目,驗證送貨位址和時間是否在餐廳的服務區域内,驗證訂單最低要求,并獲得訂單行項目的價格。
  • Kitchen Service:建立Ticket(後廚工單)。
  • Accounting Service:授權消費者的信用卡。

同樣,為了實作acceptOrder()系統操作,Kitchen Service必須調用Delivery Service來安排送餐員傳遞訂單。表2-3顯示了服務、修訂後的API及協作者。為了完整定義服務API,你需要分析每個系統操作并确定所需的協作。

帶你讀《微服務架構設計模式》之二:服務的拆分政策第2章

到目前為止,我們已經确定了每項服務實作的服務和操作。但重要的是要記住,我們勾勒出的架構非常抽象。我們沒有選擇任何特定的程序間通信技術。此外,即使術語操作表明某種基于同步請求和響應的程序間通信機制,你也會發現異步消息起着重要作用。在本書中,我将會介紹可能影響這些服務協作方式的架構和設計概念。

第3章将介紹特定的程序間通信技術,包括REST等同步通信機制和使用消息代理的異步消息。我将讨論同步通信如何影響可用性并引入自包含服務的概念,該服務不會同步調用其他服務。實作自包含服務的一種方法是使用第7章中介紹的CQRS模式。例如,Order Service可以維護Restaurant Service所擁有的資料的副本,以便消除同步調用Restaurant Service進行訂單驗證的需要。通過訂閱Restaurant Service在其資料發生更新時對外釋出的事件,Order Service可以維護Restaurant Service的一份資料副本。

第4章将介紹Saga概念以及它如何使用異步消息來協調參與Saga的服務。除了可靠地更新分散在多個服務中的資料之外,Saga也是實作自包含服務的一種方式。例如,我将描述如何使用Saga實作createOrder()操作,該Saga使用異步消息調用服務,例如Consumer Service、Kitchen Service和Accounting Service。

第8章将描述API Gateway的概念,它将API公開給外部用戶端。API Gateway可以使用第7章中描述的API組合模式實作查詢操作,而不是簡單地将其路由到服務。API Gateway中的邏輯通過調用多個服務并組合結果來收集查詢所需的資料。在這種情況下,系統操作被配置設定給API Gateway而不是服務。服務需要實作API Gateway所需要的查詢操作。

本章小結

  • 架構決定了軟體的各種非功能性因素,比如可維護性、可測試性、可部署性和可擴充性,它們會直接影響開發速度。
  • 微服務架構是一種架構風格,它給應用程式帶來了更高的可維護性、可測試性、可部署性和可擴充性。
  • 微服務中的服務是根據業務需求進行組織的,按照業務能力或者子域,而不是技術上的考量。
  • 有兩種分解模式:
    • 按業務能力分解,其起源于業務架構。
    • 基于領域驅動設計的概念,通過子域進行分解。
  • 可以通過應用DDD并為每個服務定義單獨的領域模型來消除上帝類,正是上帝類引起了阻礙分解的交織依賴項。