歡迎光臨
每天分享高質量文章

eShopOnContainers 知多少[8]:Ordering microservice

1. 引言

Ordering microservice(訂單微服務)就是處理訂單的了,它與前面講到的幾個微服務相比要複雜的多。主要涉及以下業務邏輯:

  1. 訂單的創建、取消、支付、發貨
  2. 庫存的扣減

2. 架構樣式

如上圖所示,該服務基於CQRS 和DDD來實現。

從專案結構來看,主要包括7個專案:

  1. Ordering.API:應用層
  2. Ordering.Domain:領域層
  3. Ordering.Infrastructure:基礎設施層
  4. Ordering.BackgroundTasks:後臺任務
  5. Ordering.SignalrHub:基於Signalr的訊息推送和實時通信
  6. Ordering.FunctionalTests:功能測試專案
  7. Ordering.UnitTests:單元測試專案

從以上的專案定義來看,該微服務的設計並符合DDD經典的四層架構。

核心技術選型:

  1. ASP.NET Core Web API
  2. Entity Framework Core
  3. SQL Server
  4. Swashbuckle(可選)
  5. Autofac
  6. Eventbus
  7. MediatR
  8. SignalR
  9. Dapper
  10. Polly
  11. FluentValidator

3. 簡明DDD

領域驅動設計是一種方法論,用於解決軟體複雜度問題。它強調以領域為核心驅動設計。主要包括戰略和戰術設計兩大部分,其中戰略設計指導我們在宏觀層面對問題域進行識別和劃分,從而將大問題劃分為多個小問題,分而治之。而戰術設計從微觀層面指導我們如何對領域進行建模。其中戰術設計了引入了很多核心要素,指導我們建模:

  1. 值物件(Value Object)
  2. 物體(Entity)
  3. 領域服務(Domain Service)
  4. 領域事件(Domain Event)
  5. 資源庫(Repository)
  6. 工廠(Factory)
  7. 聚合(Aggregate)
  8. 應用服務(Application Service) 

其中物體、值物件和領域服務用於表示領域模型,來實現領域邏輯。
聚合用於封裝一到多個物體和值物件,確保業務完整性。
領域事件來豐富領域物件之間的交互。
工廠、資源庫用於管理領域物件的生命周期。
應用服務是用來表達用例和用戶故事。

有了以上的戰術設計要素還不夠,如果它們糅合在一起,還是會很混亂,因此DDD再通過分層架構來確保關註點分離,即將領域模型相關(物體、值物件、聚合、領域服務、領域事件)放到領域層,將資源庫、工廠放到基礎設施層,將應用服務放到應用層。以下就是DDD經典的四層架構:

以上相關圖片來源於:張逸 · 領域驅動戰略設計實踐

4. Ordering.Domain:領域層

如果對訂單微服務應用DDD,那麼要摒棄傳統的面向資料庫建模的思想,轉向領域建模。該專案中主要定義了以下領域物件:

  • Order:訂單
  • OrderItem:訂單項
  • OrderStatus:訂單狀態
  • Buyer:買家
  • Address:地址
  • PaymentMethod:支付方式
  • CardType:銀行卡片型別

在該示例專案中,定義了兩個聚合:訂單聚合和買家聚合,其中Order和Buyer分屬兩個聚合根,其中訂單聚合通過持有買家聚合的唯一ID進行關聯。如下圖所示:

我們依次來看其對物體、值物件、聚合、資源庫、領域事件的實現方式。

4.1. 物體、值物件與聚合

物體與值物件最大的區別在於,物體有識別符號可變,值物件不可變。為了保證領域的不變性,也就是更好的封裝,所有的屬性欄位都設置為 privateset,集合都設置為只讀的,通過建構式進行初始化,通過暴露方法供外部呼叫修改。
從類圖中我們可以看出,其主要定義了一個 Entity抽象基類,所有的物體通過繼承 Entity來實現命名約定。這裡面有兩點需要說明:

  1. 通過 Id屬性確保唯一識別符號
  2. 重寫 Equals和 GetHashCode方法(hash值計算: this.Id.GetHashCode()^31)
  3. 定義 DomainEvents來儲存物體關聯的領域事件(領域事件的發生歸根結底是由於領域物件的狀態變化引起的,而領域物件[物體、值物件和聚合])中值物件是不可變的,而聚合往往包含多個物體,所以將領域事件關聯在物體上最合適不過。)

同樣,值物件也是通過繼承抽象基類 ValueObject來進行約定。其主要也是多載了 EqualsGetHashCode和方法。這裡面有必要學習其 GetHashCode的實現技巧:

赞(0)

分享創造快樂