領域驅動設計實踐:支付系統建模 - Xiao

banq發表於2022-03-18

在Airwallex,領域驅動設計(DDD)方法被用來指導如何對複雜的業務問題和系統設計進行建模。

在這篇部落格中,我們試圖全面介紹用DDD模式對支付系統進行建模的做法。

 

簡介

支付系統是一個相當複雜和多變的系統,從訂單、欺詐、通知、與各種支付方式的整合到資金清算和結算,涉及面很廣。

在處理一個複雜的系統時,大多數開發人員可能會遇到一些問題

  • 邊界和責任不明確,只是一個有許多模型和業務邏輯的大應用程式。
  • 沒有隔離和模組化:複雜的業務工作流和流程是混合的,難以擴充套件。
  • 沒有關注點的分離:核心業務邏輯與技術實現細節混在一起。

軟體行業中的許多設計模式都能解決這些問題,在Airwallex,我們嘗試採用領域驅動設計(DDD)的方法來為我們的支付系統建模,以管理系統設計中的複雜性。

 

什麼是DDD

領域驅動設計(DDD)是由埃裡克-埃文斯(Eric Evans)提出的,它是一套思想、原則和模式,有助於根據業務領域的基礎模型設計軟體系統。DDD有兩個不同的空間:問題空間和解決方案空間。

在問題空間,你是用戰略模式來定義系統的大規模結構,它專注於分析一個領域、子領域和泛在語言。

而在解決方案空間中,採用戰術模式來提供一套設計模式,你可以用它來建立領域模型。這些模式包括有界的上下文、上下文對映、實體、聚合體、領域事件、領域服務、應用服務和基礎設施。這些戰術模式將幫助你設計既鬆散耦合又有凝聚力的微服務

領域驅動設計實踐:支付系統建模 - Xiao

 

如何在實踐中應用DDD

想象一下,有這樣一個場景:

  • 一位顧客想在商家的網站上購買一件T恤,價格是10美元。
  • 顧客可以用各種支付方式來支付這件T恤,如Visa卡或微信錢包。
  • 客戶付款後,商家可以從支付閘道器獲得通知,這樣他們就可以向客戶展示付款成功的頁面。
  • 商戶可以在Airwallex Webapp中檢視付款詳情,這樣他們就可以知道這件T恤可以獲得多少資金,Airwallex扣除了多少費用,以及資金何時會被結算到他的Airwallex錢包。 

將遵循以下步驟,應用DDD對基於上述場景的支付系統進行建模。

  • 分析現實世界中的業務用例,以獲得問題空間中的域和子域。通常,在這個階段,Event Storming是一個很好的工具。
  • 定義解決方案空間中的有界上下文
  • 在有界限的上下文中,應用戰術性DDD模式來定義實體、聚合、領域服務、領域事件等。
  • 使用上一步的結果來確定你的團隊中的微服務。

以下是分析結果。

 

問題空間

  • 領域

支付系統

  • 子域

  1. 支付處理:商家可以通過各種支付方式接受客戶的付款
  2. 金融:對商家的支付資金進行清算和結算。

  • 通用語言

在與領域專家討論後,以下是所有團隊接受的通用語言。

  1. 支付意圖:商家建立的訂單,指定價格、產品、客戶等。
  2. 付款企圖:商家建立的交易,以接受客戶對特定訂單的付款。
  3. 付款方式:客戶為產品或服務付款的方式。
  4. 付款結算:一批結算到商家錢包的付款。
  5. 付款檢視:一個聚合的付款細節檢視,包含與一個付款有關的所有資料。

 

解決方案空間

  • 有界上下文

有界上下文(BC)限定了一個領域模型的範圍。從問題空間的分析結果來看,我們可以定義以下有界上下文。

  1. 支付閘道器:API閘道器,為商戶提供可靠的API,以建立或檢視付款。
  2. 支付核心:支付意圖、嘗試、方法資源管理。
  3. 支付介面卡:與一個外部PSP(微信/支付寶/Visa/Mastercard等)整合。
  4. 支付結算:為商戶計算和結算每筆支付的原則和費用。
  5. 支付融合:支付細節的聚合檢視。

而上下文地圖將是這樣的:

領域驅動設計實踐:支付系統建模 - Xiao

  • 領域模型

從上面我們分析的場景和無所不在的語言中,我們可以確定以下聚合、實體、價值物件和領域事件

領域驅動設計實踐:支付系統建模 - Xiao

  • 領域服務

在我們的實踐中,域服務是為一個聚合體提供的無狀態業務邏輯服務,遵循單一責任模式。通常情況下,我們會在領域服務中封裝領域倉庫、聚合變化和領域事件釋出。以PaymentAttemptExecutorService為例。

領域驅動設計實踐:支付系統建模 - Xiao

  • 領域事件

領域事件可以使系統更具可擴充套件性,並避免任何耦合--一個聚合體不應該決定其他聚合體應該做什麼,以及時間耦合--付款的成功完成並不取決於所有程式在同一時間可用。

領域驅動設計實踐:支付系統建模 - Xiao

例如,當PaymentCaptureCommand將支付狀態改為已支付時,領域事件PaymentAttemptCapturedEvent被髮送,以通知聚合的PaymentAttempt被捕獲。在PaymentAttemptCapturedEvent的領域事件處理程式中,我們可以把副作用放在業務邏輯上,比如通知支付融合的邊界上下文來更新支付細節和支付結算的邊界上下文來計算結算金額和費用。

領域驅動設計實踐:支付系統建模 - Xiao

  • 基礎設施

在DDD模式中,基礎設施層被用來將核心業務領域與技術實現細節分開。通常,該層採用反汙層(ACL)模式。以領域儲存庫為例。

領域驅動設計實踐:支付系統建模 - Xiao

領域倉庫只定義了介面,比如他們能做什麼,但實現細節應該隱藏在基礎設施層裡面,比如使用PostgreSQL或MongoDB來儲存資料。例如,在基礎設施層,PaymentAttemptPgRepository是基於PostgreSQL的具體實現,toPO是用於將域物件PaymentAttempt轉換為持久化物件的對映器。

領域驅動設計實踐:支付系統建模 - Xiao

因此,在領域層,我們只關注領域模型,它與基礎設施技術完全脫鉤。當基礎設施層有任何變化時,不需要在領域層中進行改變。

 

從領域模型到微服務

現在,我們已經為支付系統定義了一組有邊界的上下文,並在每個有邊界的上下文中確定了一組實體、集合體和領域事件服務。

下一步就是要從領域模型到應用微服務的設計。

在這裡,我們選擇將一個有界上下文對映到一個微服務。

領域驅動設計實踐:支付系統建模 - Xiao

 

結論

在這篇部落格中,當我們試圖對支付系統進行建模時,我們觸及了領域驅動設計(DDD)模式的各種概念和策略。採用DDD可以提供許多好處,例如,在所有的團隊中進行清晰的溝通,以及在設計系統時提供一個成熟的模式來管理複雜性和提供更好的可擴充套件性。

  • 有了無處不在的語言,我們可以實現更多的自我描述的類名和函式名。
  • 通過聚合模式,我們可以實現清晰的邊界和單一的責任。
  • 通過領域事件模式,我們可以將核心業務流程與聚合體上的副作用分開。
  • 通過基礎設施層和ACL模式,我們可以將核心業務領域模型與技術實現細節分開。
  • 通過有邊界的上下文模式,我們可以推匯出潛在的微服務候選人。

DDD模式是一個龐大的話題,我認為我們做得還不夠充分,無法全面解釋它們,但我們想介紹一些關鍵的話題和我們實踐該模式的經驗。在未來,我們將繼續深入研究DDD模式中的每一個主題,如層管理、領域事件儲存、上下文對映模式等。

相關文章