當中臺遇上DDD,我們該如何設計微服務?

微服務架構有哪些模型?中臺、領域驅動設計及微服務之間有着什麼樣的關係?微服務的邊界設計怎麼做?怎麼做設計和拆分?且看作者爲你娓娓道來。

借用當下最流行的段子做個開場白。

“設計原則千萬條,高內聚低耦合第一條,架構設計不規範,開發運維兩行淚!”。

在分佈式架構下,單體應用被拆分爲多個微服務,爲了保證微服務的單一職責和合理拆分,“高內聚、鬆耦合”是最寶貴的設計原則。

通俗點講,高內聚就是把相關的行爲聚集在一起,把不相關的行爲放在別處,如果你要修改某個服務的行爲,最好只在一處修改。如果做到了服務之間的鬆耦合,那麼修改一個服務就不需要修改另一服務,一個鬆耦合的服務應該儘可能少的知道與之協作的那些服務的信息。

從集中式架構向分佈式架構的技術轉型,正如從蓋磚瓦房向蓋高樓大廈轉變一樣,必然要有組織、文化、理念和設計方法的同步更新,其中最不可或缺的能力就是架構設計能力。

如何做到“高內聚、低耦合”?我們先來學習幾種典型的微服務架構模型。

微服務架構模型

整潔架構(又名洋蔥架構)

在整潔架構裏,同心圓代表應用軟件的不同部分,從裏到外依次是領域模型、領域服務、應用服務、最外圍是容易變化的內容,如界面和基礎設施(如數據存儲等)。整潔架構是以領域模型爲中心,不是以數據爲中心。

1

整潔架構

整潔架構最主要原則是依賴原則,它定義了各層的依賴關係,越往裏,依賴越低,代碼級別越高。外圓代碼依賴只能指向內圓,內圓不知道外圓的任何事情。一般來說,外圓的聲明(包括方法、類、變量)不能被內圓引用。同樣的,外圓使用的數據格式也不能被內圓使用。

整潔架構各層主要職能如下:

Entities:實現領域內核心業務邏輯,它封裝了企業級的業務規則。一個Entity可以是一個帶方法的對象,也可以是一個數據結構和方法集合。

Use Cases:實現與用戶操作相關的服務組合與編排,它包含了應用特有的業務規則,封裝和實現了系統的所有用例。

Interface Adapters:它把適用於Use Cases和entities的數據轉換爲適用於外部服務的格式,或把外部的數據格式轉換爲適用於Use Casess和entities的格式。

Frameworks and Drivers:這是實現所有前端業務細節的地方:UI,Tools,Frameworks等。

六邊形架構(又名端口適配器架構)

追溯微服務架構的淵源,一般會涉及到六邊形架構。六邊形架構的核心理念是:應用是通過端口與外部進行交互的,這也是微服務架構下API網關盛行的主要原因。六邊形架構中,內部業務邏輯(應用層和領域模型)與外部資源(APP,WEB應用以及數據庫資源等)完全隔離,僅通過適配器進行交互。它解決了業務邏輯與用戶界面的代碼交錯的主要問題,從而可以很好的實現前後端分離。

2

六邊形架構

六邊形架構將系統分爲內部和外部兩層六邊形,內部六邊形代表了應用的核心業務邏輯,外部六邊形代表外部應用、驅動和基礎資源等。內部通過端口和適配器與外部通信,對應用以API主動適配的方式提供服務,對資源通過依賴反轉被動適配資源的形式呈現。一個端口可能對應多個外部系統,不同的外部系統使用不同的適配器,適配器負責對協議進行轉換。這就使得應用程序能夠以一致的方式被用戶、程序、自動化測試、批處理腳本所驅動。

六邊形架構各層的依賴關係與整潔架構類似。

CQRS(命令與查詢職責分離)

CQRS就是讀寫分離,讀寫分離的主要目的是爲了提高查詢性能,同時達到讀、寫解耦。而DDD和CQRS結合,可以分別對讀和寫建模。

3

CQRS(命令與查詢職責分離)

查詢模型是一種非規範化數據模型,它不反映領域行爲,只用於數據查詢和顯示;命令模型執行領域行爲,在領域行爲執行完成後通知查詢模型。

命令模型如何通知到查詢模型呢?如果查詢模型和領域模型共享數據源,則可以省卻這一步;如果沒有共享數據源,可以藉助於發佈訂閱的消息模式通知到查詢模型,從而達到數據最終一致性。

Martin在blog中指出:CQRS適用於極少數複雜的業務領域,如果不是很適合反而會增加複雜度;另一個適用場景是爲了獲取高性能的查詢服務。

對於寫少讀多的共享類通用數據服務(如主數據類應用)可以採用讀寫分離架構模式。單數據中心寫入數據,通過發佈訂閱模式將數據副本分發到多數據中心。通過查詢模型微服務,實現多數據中心數據共享和查詢。

領域驅動設計分層架構

分層架構的一個重要原則是每層只能與位於其下方的層發生依賴。

分層架構的好處是顯而易見的。

首先,由於層間鬆散的耦合關係,使得我們可以專注於本層的設計,而不必關心其他層的設計,也不必擔心自己的設計會影響其它層,對提高軟件質量大有裨益。其次,分層架構使得程序結構清晰,升級和維護都變得十分容易,更改某層的代碼,只要本層的接口保持穩定,其他層可以不必修改。即使本層的接口發生變化,也隻影響相鄰的上層,修改工作量小且錯誤可以控制,不會帶來意外的風險。
 
關於分層架構的權威觀點,Martin Fowler在《Patterns of Enterprise Application Architecture》一書中給出了答案: 1. 開發人員只關注整個架構中的某一層。 2. 很容易的用新的方法來替換原有層次的方法。 3. 降低層與層之間的依賴。 4. 有利於標準化。 5. 利於各層邏輯的複用。

要保持程序分層架構的優點,就必須堅持層間的鬆耦合關係。設計程序時,應先劃分出可能的層次,以及此層次提供的接口和需要的接口。設計某層時,應儘量保持層間的隔離,僅使用下層提供的接口。

4

DDD(領域驅動設計)分層架構

DDD分層架構各層定義與職能:

展現層:它負責向用戶顯示信息和解釋用戶命令,完成前端界面邏輯。這裏的用戶不一定是使用用戶界面的人,也可以是另一個計算機系統。

應用層:它是很薄的一層,負責展現層與領域層之間的協調,也是與其它系統應用層進行交互的必要渠道。應用層要儘量簡單,不包含業務規則或者知識,不保留業務對象的狀態,只保留有應用任務的進度狀態,更注重流程性的東西。它只爲領域層中的領域對象協調任務,分配工作,使它們互相協作。

領域層:它是業務軟件的核心所在,包含了業務所涉及的領域對象(實體、值對象)、領域服務以及它們之間的關係,負責表達業務概念、業務狀態信息以及業務規則,具體表現形式就是領域模型。領域驅動設計提倡富領域模型,即儘量將業務邏輯歸屬到領域對象上,實在無法歸屬的部分則以領域服務的形式進行定義。

基礎設施層:它向其他層提供通用的技術能力,爲應用層傳遞消息(API網關等),爲領域層提供持久化機制(如數據庫資源)等。

架構模型對比和分析

雖然整潔架構、六邊形架構以及DDD分層架構三種架構模型展現方式以及解決問題的出發點不一樣,但其架構思想與微服務架構高內聚低耦合的設計原則高度一致。

5

整潔、六邊形以及DDD三種架構模型關係

突破現象看本質,在變與不變中尋找平衡!

從上圖可以看出,在六邊形架構、DDD分層架構的白框部分以及整潔架構Use Cases和Entities區域實現了核心業務邏輯。但是核心業務邏輯又由兩部分來完成:應用層和領域層邏輯。領域層實現了最核心的業務領域部分的邏輯,對外提供領域模型內細粒度的領域服務,應用層依賴領域層業務邏輯,通過服務組合和編排通過API網關向前臺應用提供粗粒度的服務。

系統需求變幻無窮,但變化總是有矩可循的,用戶體驗、操作習慣、市場環境以及管理流程的變化,往往會導致界面邏輯和流程的多變,但總體來說,不管前臺如何變化,核心領域邏輯基本不會大變。把握好這個規律,我們就知道如何設計應用層和領域層,如何進行邏輯劃界了。
 
上述三種架構模型正是通過分層方式來控制需求變化對系統的影響,確保從外向裏受需求影響逐步減小。面向用戶的展現層可以快速響應外部需求進行調整和發佈,靈活多變,應用層通過服務組合和編排實現業務流程的快速適配上線,領域層基本就不需要太多的變化了。這樣設計的好處是可以保證領域層的核心業務邏輯不會因爲外部需求和流程的變動而調整,對於建立前臺靈活、中臺穩固的架構能力是很有好處的。

從幾種架構模型看如何進行中臺及微服務設計?

中臺和微服務設計的關鍵在於合理的分層和領域模型的設計!

聚焦領域模型

中臺屬於後端業務領域邏輯範疇,重點關注領域內業務邏輯的實現,通過實現公共需求爲前臺應用提供共享服務能力。按DDD的方法,在領域模型建立的過程中會對業務和應用進行清晰的邏輯和物理邊界劃分。領域模型的設計結果會影響到後續的系統模型、架構模型和領域層代碼模型的設計,最終影響到微服務的拆分和項目落地實施。

合理的架構分層

不要把與領域無關的業務邏輯放在領域層,避免領域業務邏輯被污染,保證領域層的純潔,只有這樣才能降低領域邏輯受外部變化的影響。在領域和架構模型建立後,代碼模型的邏輯分層和微服務拆分要具體情況具體分析,根據自身研發和運維能力綜合考慮。

(1) 項目級單應用

對於單應用系統的分層,遵循上述分層架構模型即可,核心領域邏輯在領域層實現,服務的組合和編排在應用層實現,兩者組合形成中臺,通過API對前臺應用提供服務。

從部署和微服務拆分來講,領域層代碼部署時可能是一個微服務,也可能會根據限界上下文被拆分爲多個微服務部署。應用層代碼如果邏輯複雜,含較多個性業務邏輯,可以根據需要獨立爲微服務部署。如果邏輯簡單,且領域層是一個微服務,在劃分好應用層和領域層代碼邏輯邊界的情況下,如果符合微服務拆分原則,也可以考慮將應用層與領域層代碼合併爲一個微服務部署。

(2)企業級多中臺應用

對於企業級多中臺應用,多箇中臺應用通過API網關對外發布API服務。核心域業務中臺在調用支撐域和通用域中臺服務時通過核心域應用層完成多中臺服務的組合和編排,爲前臺應用提供API服務。核心域中臺的應用層是否獨立成微服務部署,需考慮的情況與單應用系統相似。

服務的管理

應用層、領域層和基礎設施層都有對應的服務,各司其職提供服務,其中基礎設施層的服務通過依賴反轉模式爲領域層和應用層提供基礎設施資源服務。應用層和領域層服務發佈在API網關,通過API網關適配,爲前臺提供用戶無差異化(應用app、批處理或自動化測試)的服務。

資源的適配和解耦

由於上述架構模型中定義的外層只能依賴內層的架構原則,對於像數據庫、緩存、文件系統等的外部基礎設施資源,往往採用依賴反轉的模式對外提供資源服務,實現應用層、領域層與基礎設施層資源的解耦。在設計中應考慮資源層的代碼適配邏輯,一旦基礎設施資源出現變更(如換數據庫),可以屏蔽資源變更對業務代碼帶來的影響,切斷業務邏輯對基礎資源的依賴,降低由於資源變更對業務邏輯的影響。

前臺應用

從核心業務邏輯來看,中臺實現了主要的業務邏輯,屬於標準化的重量級應用。前臺應用聚焦於界面交互以及業務流程等,屬於輕量級應用,前臺應用可以有個性的業務邏輯、流程和配置數據,甚至數據庫,通過調用中臺API服務完成交互界面和業務全流程。

中臺、領域驅動設計及微服務

分析和設計模式的演進

在單機和集中式架構時代,系統分析和設計往往都是分階段割裂進行的,容易導致需求、設計與代碼實現的不一致,軟件上線後才發現很多功能不是自己想要的,而且在這種模式下,軟件也不能快速響應需求和業務變化。
 
領域驅動設計(DDD)打破了這種隔閡,它提出了領域模型概念,統一了分析、設計和開發語言和過程,使得軟件能夠更靈活快速響應需求變化。

軟件分析和設計方法經歷了三個階段的演進:

第一階段是單機架構時代:採用面向過程的設計方法,系統包括UI層和數據庫兩層,採用C/S架構模式,整個系統圍繞數據庫驅動設計和開發,新項目總是從設計數據庫及其字段開始。

第二階段是集中式架構時代:採用面向對象的設計方法,系統包括UI層、業務邏輯層和數據庫層,採用經典的三層架構,也有部分應用採用傳統的SOA架構,這種架構易使服務變得臃腫,難於維護拓展,伸縮性能差。這個階段系統分析、軟件設計和開發大多是分階段進行的。

第三階段是分佈式架構時代:由於微服務架構的流行,採用領域驅動設計方法,應用系統包括UI層、應用層、領域層和基礎層。這個階段融合了分析和設計階段,通過建立領域模型,劃分領域邊界,做到領域模型既設計,代碼與設計保持一致。

領域驅動設計主要優勢:1.業務導向。2.業務邏輯內聚,應用邊界清晰。3.建立領域模型優先。4.分析、設計、代碼和數據有機結合。5.代碼即設計。6.擴展性好。

數據驅動設計主要特點:1.技術導向。2.數據庫優先。3.代碼不能反映業務和設計。4.業務邏輯分散。5.擴展性不好。

領域驅動設計概述

2004年Eric Evans 發表《Domain-Driven Design –Tackling Complexity in the Heart of Software》 (領域驅動設計 )簡稱Evans DDD。但在軟件開發領域一直都是雷聲大,雨點小,領域驅動設計核心思想是通過領域驅動設計方法定義領域模型,從而確定業務和應用邊界,保證業務模型與代碼模型的一致性。這幾年之所以開始火起來,主要功勞要歸功於隊友“微服務”,領域驅動設計與微服務架構天生匹配。

領域驅動設計(DDD)是一種處理高度複雜域的設計思想,試圖分離技術實現的複雜性,圍繞業務概念構建領域模型來控制業務的複雜性,以解決軟件難以理解,難以演化等問題。團隊利用它可以成功的開發複雜業務軟件系統,在系統變大時仍能保持敏捷性。

領域驅動設計分爲兩個階段:

1.以一種領域專家、設計人員、開發人員都能理解的通用語言作爲相互交流的工具,在交流的過程中發現領域概念,然後將這些概念設計成一個領域模型;

2.由領域模型驅動軟件設計,用代碼來實現該領域模型。

領域驅動設計的核心訴求是讓業務架構和系統架構形成綁定關係,當我們去響應業務變化調整業務架構時,系統架構的改變也會隨之發生。在領域驅動設計中業務架構的梳理和系統架構的梳理是同步進行的,其結果是設計出的業務上下文和系統模塊結構是綁定的。同時技術架構也是解耦的,可以根據劃分出來的業務上下文的系統架構選擇最合適的實現技術。

領域驅動設計包括戰略設計和戰術設計兩個部分。戰略設計主要關注按領域定義,在限界上下文內形成統一語言,提升業務和技術的溝通效率; 戰術設計主要關注領域設計在落地時與設計模型及實現模型的差異性,減小業務和技術之間的鴻溝。(本文對DDD知識點不做詳述,如需瞭解或學習,請查閱《領域驅動設計:軟件核心複雜性應對之道》和《實現領域驅動》)。

領域驅動設計可能會給你帶來以下收穫:

1、領域驅動設計是一套完整而系統的設計方法,它能帶給你從戰略設計到戰術設計的規範過程,使得你的設計思路能夠更加清晰,設計過程更加規範。

2、領域驅動設計尤其善於處理與領域相關的高複雜度業務的產品研發,通過它可以爲你的產品建立一個核心而穩定的領域模型內核,有利於領域知識的傳遞與傳承。

3、領域驅動設計強調團隊與領域專家的合作,能夠幫助團隊建立一個溝通良好的團隊組織,構建一致的架構體系。 領域驅動設計強調對架構與模型的精心打磨,尤其善於處理系統架構的演進設計。

4、領域驅動設計的思想、原則與模式有助於提高團隊成員的架構設計能力。

5、領域驅動設計與微服務架構天生匹配,無論是在新項目中設計微服務架構,還是將系統從單體架構演進到微服務設計,都可以遵循領域驅動設計的架構原則。

爲什麼領域驅動設計是微服務架構的最佳設計方法?

領域驅動設計作爲一種架構設計方法,微服務作爲一種架構風格,兩者從本質上都是爲追求高響應力目標而從業務視角去分離複雜度的手段。 兩者都強調從業務出發,其核心要義強調根據業務發展,合理劃分領域邊界,持續調整現有架構,優化現有代碼,以保持架構和代碼的生命力(演進式架構) 。
 
領域驅動設計主要關注:業務領域,劃分領域邊界;構建通用語言,高效溝通;對業務進行抽象,建立領域模型;維持業務和代碼的邏輯一致性。

微服務主要關注:運行時進程間通信,能夠容錯和故障隔離;去中心化管理數據和去中心化治理;服務可以獨立的開發、測試、構建和部署,按業務組織全功能團隊;高內聚低耦合,職責單一。

如果你的業務焦點在領域和領域邏輯,那麼你就可以選擇DDD進行微服務架構設計。

中臺、DDD與微服務

中臺的定義來源於阿里的中臺戰略(詳見《企業IT架構轉型之道:阿里巴巴中臺戰略思想與架構實戰》鍾華編著)。2015年年底,阿里巴巴集團對外宣佈全面啓動阿里巴巴集團2018年中臺戰略,構建符合數字時代的更具創新性、靈活性的“大中臺、小前臺”組織機制和業務機制,即作爲前臺的一線業務會更敏捷、更快速適應瞬息萬變的市場,而中臺將集合整個集團的運營數據能力、產品技術能力,對各前臺業務形成強力支撐。

中臺的本質是提煉各個業務條線的共同需求,並將這些功能打造成組件化產品,然後以API接口的形式提供給前臺各業務部門使用。前臺要做什麼業務,需要什麼資源可以直接找中臺,不需要每次去改動自己的底層,而是在底層不變動的情況下,在更豐富靈活的“大中臺”基礎上獲取支持,讓“小前臺”更加靈活敏捷。

中臺戰略的主要目標是實現公共需求和功能的中臺化共享,減少重複建設和投入,爲前臺提供統一的一致服務。至於前臺應用是否可以有數據庫?抑或採用什麼樣的開發技術,這些都不是重點,重點需要考慮的是那些公共需求和需要共享的功能是否通過中臺的方式被前臺使用了。

領域驅動設計中領域的定義:一個領域本質上可以理解爲就是一個問題域,只要是同一個領域,那問題域就相同。所以只要我們確定了系統所屬的領域,那這個系統的核心業務,即要解決的關鍵問題、問題的範圍邊界就基本確定了。領域的本質是問題域,問題域可能根據需要逐層細分,因此領域可分解爲子域,子域或可繼續分爲子子域。。。

在領域驅動設計中根據重要性與功能屬性將領域分爲三類子域,分別是:核心子域、支撐子域和通用子域。決定產品和企業獨特競爭力的子域是核心子域,它是業務成功的主要因素和企業的核心競爭力。沒有個性化的訴求,屬於通用功能的子域是通用子域,如登陸認證。 還有一種所提供的功能是必須的,但不是通用也不是企業核心競爭力的子域是支撐子域,如單證。

6

DDD:核心域、支撐域和通用域

中臺、領域以及微服務屬於不同層面的內容,稍作分解我們理清他們之間的關係。

以保險領域爲例,業務中臺大致可分爲兩類:第一類是提供保險核心業務服務的專屬業務中臺(如承保、理賠等業務);第二類是支撐核心業務流程完成保險全流程的通用中臺(如主數據、客戶、用戶以及電子保單等)。

專屬業務中臺是保險企業的核心競爭力,對應DDD的核心子域。通用中臺對應DDD支撐子域和通用子域。不同領域可根據領域大小進一步細分多個子域,多個子域可對應到一個業務中臺,一個業務中臺也可能會分解成多個子域。

7

中臺、領域以及微服務

微服務是技術實現和部署的範疇,實現領域或中臺的業務邏輯,爲前臺應用提供服務。領域根據限界上下文可以設計爲多個微服務,而如果限界上下文過大,一個微服務也可能會包含多個子領域。

中臺是由多個業務條線的共同需求所構成,是需要共享的業務功能和服務單元的集合,一箇中臺可由一個微服務來實現,也可根據領域驅動設計和微服務拆分原則細分爲多個微服務,多個微服務功能集合共同組成一箇中臺。

基於DDD的微服務設計方法

DDD設計包括戰略設計和戰術設計兩個部分。在戰略設計階段主要完成領域建模和服務地圖。在戰術設計階段,通過聚合、實體、值對象以及不同層級的服務,完成微服務的建設和實施。通過DDD可以保證業務模型、系統模型、架構模型以及代碼模型的一致。

本部分主要討論領域設計方法,如對戰術設計和開發方法感興趣可查閱DDD戰術設計相關資料。

DDD領域設計過程包括產品願景、場景分析、領域建模和服務地圖階段,也可根據需要裁剪不必要的階段和參與角色。領域驅動設計一般經歷2-6周的時間,領域模型設計完成後,即可投入微服務實施。

1、產品願景

產品願景是對產品的頂層價值設計,對產品目標用戶、核心價值、差異化競爭點等策略層信息達成一致,避免產品在演進過程中偏離方向。

階段輸入:產品初衷、用戶研究、競品知識和差異性想法 。

參與角⾊:業務需求方、產品經理、開發組長和產品發起人。

階段產出:電梯演講畫布。

2、場景分析

場景分析是針對核心用戶及頂層服務的一種定性分析,從⽤戶視角出發,探索問題域中的典型場景分析。同時也是從用戶視角對問題域的探索,產出問題域中需要支撐的場景分類及典型場景,用以支撐領域建模階段。

階段輸⼊:核⼼干係人和服務價值定位。

參與角色:產品經理、開發組長和測試組長。

階段產出:場景分類清單。

3、領域建模

領域建模是通過對業務和問題域進⾏分析,建⽴領域模型,向上通過限界上下⽂指導微服務的邊界設計,向下通過聚合指導實體的對象設計。領域建模主要採用事件風暴方法。

階段輸入:業務領域知識和場景分類清單。

參與角色:領域專家、架構師、產品經理、開發組長和測試組長。

階段產出:聚合模型和限界上下⽂地圖。

4、服務地圖

服務地圖是整個產品服務架構的體現。結合業務與技術因素,對服務的粒度、邊界劃分、集 成關係進⾏梳理,得到反映系統微服務層面設計的服務地圖。

階段輸⼊:限界上下⽂地圖。

參與角⾊:產品經理、開發組長、測試組長和產品發起人。

階段產出:服務地圖。

在進行服務地圖設計時需要考慮以下要素:1. 圍繞限界上下⽂邊界。2. 考慮不同業務變化速度/相關度、發佈頻率。3. 考慮系統非功能性需求,如系統彈性伸縮要求、安全性要求和可⽤性要求。4. 考慮團隊組織和溝通效率。5. 軟件包限制。6.技術和架構的異構。

通過DDD戰略和戰術全流程設計可建立業務架構與系統架構的一一映射,保證業務和代碼模型的一致性。

7

DDD的業務架構與系統架構映射建立過程

DDD分層架構中的服務

前面我們談到了DDD的分層架構,分層架構主要包括:展現層、應用層、領域層和基礎層(參考圖:DDD(領域驅動設計)分層架構),各層都有不同的服務,但由於各層職責不一樣,服務目的和實現方式也存在差異。

1、應用層服務

應用層是很瘦的一層,其服務主要用來表述應用和用戶行爲。它主要負責服務的組合、編排和轉發,負責處理業務用例的執行順序以及結果的拼裝,拼裝完領域服務後以粗粒度的服務通過API網關向前臺應用發佈。通過這樣一種方式,隱藏了領域層的複雜性及其內部實現機制。 應用層除了定義應用服務之外,在這層還可以進行安全認證,權限校驗,持久化事務控制或向其他系統發送基於事件的消息通知。

2、領域層服務

領域層是較“胖”的一層,它實現了全部業務邏輯並且通過各種校驗手段保證業務正確性。業務邏輯包括:業務流程、業務策略、業務規則、完整性約束等。 當領域中的某個操作過程或轉換過程不是實體或值對象的職責時,便將該操作放在一個單獨的服務接口中,這就是領域服務,領域服務是無狀態的。

3、基礎設施層服務
 
基礎設施層服務位於基礎設施層,根據依賴倒置原則,封裝基礎資源服務,實現資源層與應用層和領域層的調用依賴反轉,爲應用層和領域層提供基礎資源服務(如數據庫、緩存等基礎資源),實現各層的解耦,降低外部資源的變化對核心業務邏輯的影響。

4、總結

應用層服務是展現層和領域層的橋樑,通過調用領域對象和領域層服務來表達用例和用戶故事。領域對象負責單一操作, 領域層服務用於協調多個領域對象共同完成某個業務操作。 應用服務原則上不處理業務邏輯,領域服務處理業務邏輯。

微服務的邊界設計

邏輯邊界與物理邊界

在領域模型設計時,我們通常會根據限界上下文將領域分解成不同的子域,劃分業務領域的邏輯邊界。在限界上下文內不同的實體和值對象可以組合成不同的聚合,從而形成聚合與聚合之間的邏輯邊界。一般來說,限界上下文可以作爲微服務拆分的依據,而限界上下文內的聚合由於其業務邏輯的高度內聚,也可以根據需要將同一領域內的聚合業務邏輯代碼拆分爲微服務,聚合是領域中可以拆分爲微服務的最小單元。

限界上下文與限界上下文之間以及聚合與聚合之間的邊界是邏輯邊界,微服務與微服務的邊界是物理邊界。邏輯邊界強調業務領域邏輯或代碼分層的隔離,物理邊界強調部署和運行的隔離。

微服務設計時是否一定要做到邏輯邊界與物理邊界一致?

邏輯邊界的劃分是否可以細於物理邊界?

過度的微服務拆分會導致服務、安全和運維管理更復雜,領域之間的服務協同或應用層的處理邏輯更復雜,總之一句話就是:需要更高的研發技能要求和軟件維護成本。因此領域和代碼分層的邏輯邊界的細分是必要的,但是物理邊界不宜過細,也就是說在不違反微服務拆分原則的情況下,不宜過度拆分微服務。

爲什麼要細分業務和代碼邏輯邊界?

在從單體向微服務演進後,隨着新需求的出現,新的微服務會開始慢慢的膨脹起來,有一天你會發現膨脹的微服務有一部分業務能力需要拆分出去時,如果沒有提前進行邏輯邊界的細分,微服務內代碼的過度耦合將會讓你無從下手,你是否還需要再做一次從單體向微服務的拆分?

如果你在微服務設計時已經根據業務領域邊界提前進行了領域代碼的分層和邏輯隔離,在微服務再次拆分時,分別對邏輯分離的領域代碼打包,同步進行數據庫拆分,就可以快速完成微服務的拆分,而不需要重複從單體應用向微服務痛苦的演進過程。

當然,在同一個微服務內邏輯隔離的代碼,在內部領域服務之間調用以及數據訪問設計上需要有合理的鬆耦合的設計和開發規範,否則也不能很快的完成微服務再次拆分。

總之,我們需要內外部邏輯邊界清晰的微服務,而不是從一個大單體重構爲多個小單體。

要做微服務而不是小單體

很多時候大家對微服務設計的理解都以爲只要最後確定拆分出多少個微服務就可以了,其實拆成多少個微服務並不是微服務架構的要點。如何設計或拆分才能避免拆分出來的微服務不是小單體?這纔是所有微服務架構團隊需要關注和解決的問題,這也是DDD的價值所在。

9

要做微服務而不是小單體

評判微服務設計合理的一個簡單標準就是:微服務在隨着業務發展而不斷拆分或者重新組合過程中不會過度增加軟件維護成本,並且這個過程是非常輕鬆且簡單的。

微服務代碼邏輯分層和結構

爲了方便在微服務變大時實現快樂的拆分和合並,在明確各層代碼職責後,我們需要對微服務代碼合理分層和邏輯隔離,以下圖爲例對代碼分層和結構進行簡要說明。

基礎層代碼:本層主要包括兩類適配代碼:主動適配和被動適配。主動適配代碼主要面向前端應用提供API網關服務,進行簡單的前端數據校驗、協議以及格式轉換適配等工作。被動適配主要面向後端基礎資源(如數據庫、緩存等),通過依賴反轉爲應用層和領域層提供數據持久化和數據訪問支持,實現資源層的解耦。

應用層代碼:本層代碼主要通過調用領域層服務或其他中臺應用層服務,完成服務組合和編排形成粗粒度的服務,爲前臺提供API服務。本層代碼可進行業務邏輯數據的校驗、權限認證、服務組合和編排、分佈式事務管理等工作。

領域層代碼:本層代碼主要實現核心的業務領域邏輯,需要做好領域代碼的分層以及聚合之間代碼的邏輯隔離。相關的開發方法請查閱DDD戰術設計相關資料,並遵循相關設計和開發規範。

10

代碼邏輯分層和結構

對代碼進行邏輯隔離和分層的主要意義在於:

1、避免各層代碼的交叉,保持領域代碼的純潔,保證中臺領域層業務邏輯的穩定。

2、業務和代碼模型的邏輯保持一致,有利於微服務的拆分和組合。

微服務的設計和拆分

微服務拆分方法

絞殺者模式

絞殺者模式類似建築拆遷,在新建築分階段建設完成入住後,分步拆除舊建築物。

“絞殺者模式”是在遺留系統外圍,將新功能用新的方式構建爲新的服務 。通過在新的應⽤中實現新特性,保持和現有系統的鬆耦合,隨着時間的推移,新的服務逐漸“絞殺”老的系統。以此逐步地替換原有系統。 對於那些老舊龐大難以更改的遺留系統,推薦採用絞殺者模式。

修繕者模式

修繕者模式類似文物修復,將存在問題的部分建築重建或者修復後,重新加入到原有的建築中,保持建築原貌。

“修繕者模式”是在既有系統的基礎上,通過剝離新業務和功能,逐步“釋放”現有系統耦合度,解決遺留系統質量不穩定和Bug多的問題。就如修房或修路一樣,將老舊待修繕的部分進行隔離,用新的方式對其進行單獨修復。 修復的同時,需保證與其他部分仍能協同功能。 修繕模式適用於需求變更頻率不高的存量系統。

微服務拆分原則

微服務拆分過程中需嚴格遵守高內聚、低耦合原則,同時結合項目的實際情況,綜合考慮業務領域、功能穩定性、應用性能、團隊以及技術等因素。

1、基於業務領域拆分,在領域模型設計時需對齊限界上下⽂,圍繞業務領域按職責單一性、功能完整性進行拆分,避免過度拆分造成跨微服務的頻繁調用。

2、基於業務變化頻率和業務關聯拆分,識別系統中的業務需求變動較頻繁的功能,考慮業務變更頻率與相關度,並對其進行拆分,降低敏態業務功能對穩態業務功能的影響。

3、基於應用性能拆分,考慮系統⾮功能性需求,識別系統中性能壓力較大的模塊,並優先對其進行拆分,提升整體性能,縮小潛在性能瓶頸模塊的影響範圍。

4、基於組織架構和團隊規模,提高團隊溝通效率。

5、基於軟件包大小,軟件包過大,不利用微服務的彈性伸縮。

6、基於不同功能的技術和架構異構以及系統複雜度。

分佈式架構設計的關注點

企業一旦採用分佈式架構和微服務技術體系,在設計時需要關注商業模式、業務邊界、數據體系、微服務設計、前臺交互以及多活容災等多領域的協同。

1、數據是本難唸的經

分佈式架構下數據面臨的問題遠比集中式架構複雜。諸如:分佈式數據庫的選型、數據的分庫和分表、數據的同步與異步、跨庫和聯表查詢、數據的分佈與集中、在線業務數據與統計分析數據的協同、集中式數據庫向分佈式數據庫的遷移以及面向場景的集中數據複製等。

(1)分佈式數據庫的選擇

從集中式架構向分佈式架構轉型,第一步就需要考慮選擇什麼樣的分佈式數據庫。

爲解決交易型分佈式數據庫的橫向計算能力,目前主要有三種類型的分佈式數據庫:一體化交易型分佈式數據庫方案(如阿里OceanBase和華爲高斯數據庫,多采用Paxos協議實現多副本數據一致)、單機交易數據庫加數據庫中間件方案(如騰訊TDSQL和TBase等,多采用數據同步實現多副本數據一致)和單機交易數據庫加分庫基礎類庫(如ShardingSphere等,主要實現數據路由和歸集)方案。三者的使用場景基本相同,都是通過對大表數據作水平切分,業務請求動態路由到指定節點,以此達到計算能力的線性擴展。一體化方案是以數據庫和中間件一體化產品的形式解決線性擴展問題,支持多副本,高可用,提供統一的運維界面。 數據庫中間件方案是以獨立數據庫中間件結合集中式數據庫的方式來解決線性擴展問題,高可用功能由中間件和數據庫自身功能分別保證。分庫基礎類庫方案是一種類似中間件的輕量級解決方案,適合簡單快速的交易操作,在強一致性和聚合分析查詢方面較弱。

(2)數據的分庫和分庫主鍵

選擇完分佈式數據庫後,第二步就需要考慮如何按照領域模型和微服務進行數據庫的分庫設計,選擇合適的分庫主鍵將是一個關鍵技術點。

對於與客戶接觸的業務領域,個人認爲可以以客戶維度作爲數據分庫主鍵,以客戶爲實體,確保所有與本客戶接觸和服務的數據都在一個單元內,通過集中共享的中臺服務,爲所有渠道的客戶提供一致性體驗。如果後序管理流程需要基於區域管理要求,也可以考慮在後序業務環節的數據庫中以區域維度作爲數據庫分庫主鍵,滿足業務基於區域的管理要求。

如何將客戶維度的數據傳輸到以區域爲維度的數據庫中?我們可以考慮基於消息隊列的事件驅動模型。

系統如果做不到“以客戶爲中心”,又如何能實現“以客戶爲中心”的業務需求呢?

(3)高頻熱點數據的緩存

對於像產品基礎數據、主數據之類的熱點高頻訪問數據,在進行系統設計時需考慮將這些數據加載至緩存中,降低數據庫的壓力,對外提供高性能的數據訪問能力。

緩存技術的使用就像調味料一樣,投入小見效快,用戶體驗提升快。

(4)數據副本與跨庫聯表查詢

採用分佈式技術後,數據將碎片化,爲了減輕由於跨庫以及聯表查詢給分佈式數據庫的壓力,需要建立多維的全局數據視圖(如客戶統一視圖、業務統計數據視圖等)和麪向具體場景的預處理好的數據聚合副本,提供複雜場景的數據查詢服務,減輕交易型數據庫的壓力。

全局數據視圖其數據來源於各業務條線的分佈式數據庫,從源端分佈式數據庫通過準實時的方式彙集(可以基於數據庫日誌捕獲技術加消息隊列)。全局視圖的數據庫也可以是分佈式數據庫,根據業務要求選擇合適的分庫主鍵進行數據重分佈。

對於分佈式數據庫跨庫關聯查詢性能低的問題,有兩種解決方案,根據具體場景採用合適的方案:

1)面向場景的數據副本查詢庫。將這些需要關聯查詢的數據副本集中存放在一個分佈式數據庫中。在進行數據彙集時,提前做好數據關聯處理(如多表數據合併成一個寬表),通過查詢微服務,專職提供關聯查詢服務。

2)小表廣播模式。有些業務場景中少量表(如用戶、機構表等)需要跟業務數據進行關聯查詢,這種場景可以考慮在業務數據庫中新建一張複製表(無需全部字段,取必要字段即可),在主表發生變化時,可以通過發佈訂閱的消息隊列模式刷新複製表的數據,保證數據的一致性。

(5)合理的數據冗餘

完成領域模型和微服務設計後,集中式數據庫的數據將被分散到不同微服務的分佈式數據庫中。數據實體的依賴關係將被打破,如果需要調用前序或後序微服務的數據實體(如:投保微服務生成的投保單、保單管理微服務的保單需要關聯投保單,理賠的報案需要關聯保單等,或電商業務中:銷售過程中的商品、運輸過程中的貨物需要關聯商品信息),這時候就會跨庫或者跨微服務調用了,必然影響系統性能。

如何處理這些跨微服務的關鍵實體數據?

最好的方式就是數據冗餘,將前序或後序環節的關鍵數據以數據清單複製表(只需必要的關鍵數據,不需要所有明細數據)的方式冗餘存儲。冗餘的好處是,前臺頁面可以一次性獲取本領域實體數據和關聯實體清單數據,同時也可以在本庫對關聯清單數據進行查詢。只有在需要獲取關聯實體數據明細時,才調用前序或後續微服務獲取全量數據。

合理的數據冗餘可以減少跨庫查詢,提升系統性能。

(6)如何數據遷移?

從集中式數據庫向分佈式數據庫切換時,數據遷移的複雜度將大大增加。需要考慮如何進行數據遷移?現有技術條件下,是不是不做數據遷移也可以無縫切換?

傳統集中式架構數據多集中在一個集中式數據庫中,數據關聯度高。

分佈式架構下,數據會隨着微服務而同步拆分,數據將變得碎片化,存在複製表,數據重分佈,數據關聯被打破,甚至還可能需要重建數據關聯。另外,分佈式架構的容災和多中心多活要求,數據遷移時還需要考慮數據的多副本和多中心的數據複製。分佈式架構下數據遷移的複雜度大增。

互聯網公司大多采用演進式架構模式,有計劃分階段的進行技術體系的升級,很多時候用戶無感知就完成了架構的升級。而傳統企業在做技術升級時如採用絞殺者重構模式,是否必須要做數據遷移?如果不做數據遷移是否也可以順利切換?是否通過數據路由加全量數據視圖的方案就可以不做數據遷移,實現新舊並存,無縫切換?數據切換方案需要詳細設計和慎重考慮(尚在考慮中,且聽下回分解)。

(7)數據的異步和同步

分佈式架構下事件驅動設計模式是常用的方法,通過基於消息隊列的發佈訂閱模式,可以很好的實現業務異步化。非實時業務場景可以採用事件驅動的模式實現異步化,減輕數據庫壓力。

也可以通過異步模式實現準實時的數據讀寫分離,提高數據庫性能。

2、中臺和微服務要處理好邊界

條條道路通羅馬,不管走哪條路,憑感覺或拍腦袋也可以設計出微服務,拆分結果可能與按照DDD方法出來的結果類似。但是如果有好的理論和方法指導,不但做事情有矩可循的,而且可以避免走彎路。由於DDD在設計的時候已經做好了邏輯的邊界劃分,在微服務需要組合和重新拆分時也會變得容易得多。

還是有必要提一下:中臺和微服務設計可以借鑑DDD的設計原則和理念,不過戰術設計部分由於過於複雜和學習成本過高,可以參考使用。

3、前、中臺協同和前臺數據的按需加載

前臺應用未來可能多采用單頁面(SPA)的微前端(對應於微服務的前端展現,一個微服務對應一個微前端)方式,通過前端集成框架(類似門戶)實現多頁面組合,提供統一的用戶體驗,在微服務和數據庫設計時也需要協同考慮前端頁面邏輯。

爲減輕跨微服務的訪問,前端頁面展示時應以清單數據方式按需加載,後端數據設計時也應同步考慮如何組合前端數據展示。如需要展示明細數據,通過調用API服務的方式獲取全量數據,減少不必要的跨微服務調用。

另外,符合條件的應用也可考慮頁面的動靜分離和路由接入,將靜態頁面通過CDN的技術,部署在靠近用戶的機房,降低交互次數,減少跨廣域網訪問帶來的網絡延遲。

前端知識有限,就寫這麼多了,哈哈。

4、容災和多活的全局考慮

分佈式架構的高可用是在應用、數據和基礎設施的分佈式技術升級後,通過多數據中心協同來實現的。

爲了容災和多活,在設計方面需要考慮:1)合適的分佈式數據庫。2)合理的數據分庫主鍵設計,數據的多副本和同步技術。3)單元化架構設計,處理好通用中臺和專屬中臺的部署和依賴關係,實現業務的自包含,減少跨數據中心調用。4)訪問層的接入,對外部訪問進行路由、限流以及灰度發佈。5)統一的全局配置數據,每個數據中心都有實時同步的全量配置數據,實現容災和多活的一鍵切換。

5、避免過度拆分和硬件依賴

過度過細的微服務拆分帶來更多的軟件維護成本和運維壓力,過多的分佈式事務也會帶來性能和數據一致性的壓力。在進行設計時,要在保證邏輯邊界清晰的情況下,嚴控微服務的過度拆分和採用過多的分佈式事務。

分佈式架構的自動的彈性伸縮大多是通過軟件的方式去實現的,爲保證應用的彈性伸縮能力,在設計中應實現去硬件的無中心化(如可採用軟負載,就不用F5之類的硬負載),儘量通過軟件實現彈性伸縮。因爲一旦綁定硬件設備,在硬件遇到瓶頸需要自動彈性伸縮的時候,就需要人工干預,無法自動彈性伸縮。

寫在最後

正如老馬說的採用微服務的企業需具備一定的高度,如文化、組織和技術,DDD同樣也需要站一定的高度。如果高度不夠,我們是否可以站在巨人的肩上呢?

在領域模型和微服務設計時,守住領域模型和邊界,各司其職,才能長治久安!

謹記:邊界!邊界!邊界!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章