结合eShopOnWeb全面认识领域模型架构
一.項目分析
在上篇中介紹了什么是"干凈架構",DDD符合了這種干凈架構的特點,重點描述了DDD架構遵循的依賴倒置原則,使軟件達到了低藕合。eShopOnWeb項目是學習DDD領域模型架構的一個很好案例,本篇繼續分析該項目各層的職責功能,主要掌握ApplicationCore領域層內部的術語、成員職責。
1. web層介紹
eShopOnWeb項目與Equinox項目,雙方在表現層方面對比,沒有太大區別。都是遵循了DDD表現層的功能職責。有一點差異的是eShopOnWeb把表現層和應用服務層集中在了項目web層下,這并不影響DDD風格架構。
項目web表現層引用了ApplicationCore領域層和Infrastructure基礎設施層,這種引用依賴是正常的。引用Infrastructure層是為了添加EF上下文以及Identity用戶管理。?引用ApplicationCore層是為了應用程序服務?調用?領域服務處理領域業務。
在DDD架構下依賴關系重點強調的是領域層的獨立,領域層是同心圓中最核心的層,所以在eShopOnWeb項目中,ApplicationCore層并沒有依賴引用項目其它層。再回頭看Equinox項目,領域層也不需要依賴引用項目其它層。
下面web混合了MVC和Razor,結構目錄如下所示:
(1) Health checks
Health checks是ASP.NET Core的特性,用于可視化web應用程序的狀態,以便開發人員可以確定應用程序是否健康。運行狀況檢查端點/health。
//添加服務services.AddHealthChecks()
.AddCheck<HomePageHealthCheck>("home_page_health_check")
.AddCheck<ApiHealthCheck>("api_health_check");
//添加中間件
app.UseHealthChecks("/health");
?下圖檢查了web首頁和api接口的健康狀態,如下圖所示
(2) Extensions
向現有對象添加輔助方法。該Extensions文件夾有兩個類,包含用于電子郵件發送和URL生成的擴展方法。
(3) 緩存
對于Web層獲取數據庫的數據,如果數據不會經常更改,可以使用緩存,避免每次請求頁面時,都去讀取數據庫數據。這里用的是本機內存緩存。
//緩存接口類private readonly IMemoryCache _cache;
// 添加服務,緩存類實現
services.AddScoped<ICatalogViewModelService, CachedCatalogViewModelService>();
//添加服務,非緩存的實現
//services.AddScoped<ICatalogViewModelService, CatalogViewModelService>();
2. ApplicationCore層
ApplicationCore是領域層,是項目中最重要最復雜的一層。ApplicationCore層包含應用程序的業務邏輯,此業務邏輯包含在領域模型中。領域層知識在Equinox項目中并沒有講清楚,這里在重點解析領域層內部成員,并結合項目來說清楚。
下面講解領域層內部的成員職責描述定義,參考了“Microsoft.NET企業級應用架構設計 第二版”。
領域層內部包括:領域模型和領域服務二大塊。涉及到的術語:
? ? ? ? ? ? ? ? ? ? 領域模型(模型)
? 1)模塊
????????????????? ? 2)領域實體(也叫"實體")
????????????????? ? 3)值對象
????????????????? ? 4)聚合
????????????????? ? 領域服務(也叫"服務")
????????????????? ? 倉儲
下面是領域層主要的成員:
下面是聚合與領域模型的關系。最終領域模型包含了:聚合、單個實體、值對象的結合。
(1) 領域模型
領域模型是提供業務領域的概念視圖,它由實體和值對象構成。在下圖中Entities文件夾是領域模型,可以看到包含了聚合、實體、值對象。
1.1 模塊
模塊是用來組織領域模型,在.net中領域模型通過命令空間組織,模塊也就是命名空間,用來組織類庫項目里的類。比如:
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregatenamespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate
1.2 實體
實體通常由數據和行為構成。如果要在整個生命周期的上下文里唯一跟蹤它,這個對象就需要一個身份標識(ID主鍵),并看成實體。 如下所示是一個實體:
/// <summary>/// 領域實體都有唯一標識,這里用ID做唯一標識
/// </summary>
public class BaseEntity
{
public int Id { get; set; }
}
/// <summary>
/// 領域實體,該實體行為由Basket聚合根來操作
/// </summary>
public class BasketItem : BaseEntity
{
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public int CatalogItemId { get; set; }
}
? 1.3 值對象
值對象和實體都由.net 類構成。值對象是包含數據的類,沒有行為,可能有方法本質上是輔助方法。值對象不需要身份標識,因為它們不會改變狀態。如下所示是一個值對象
1.4 聚合
在開發中單個實體總是互相引用,聚合的作用是把相關邏輯的實體組合當作一個整體對待。聚合是一致性(事務性)的邊界,對領域模型進行分組和隔離。聚合是關聯的對象(實體)群,放在一個聚合容器中,用于數據更改的目的。每個聚合通常被限制于2~3個對象。聚合根在整個領域模型都可見,而且可以直接引用。
在該項目中領域模型與“Microsoft.NET企業級應用架構設計第二版”書中描述的職責有不一樣地方,來看一下:
(1) 領域服務有直接引用聚合中的實體(如:BasketItem)。書中描述是聚合中實體不能從聚合之處直接引用,應用把聚合看成一個整體。
(2) 領域實體幾乎都是貧血模型。書中描述是領域實體應該包括行為和數據。
(2) 領域服務
領域服務類方法實現領域邏輯,不屬于特定聚合中(聚合是屬于領域模型的),很可能跨多個實體。當一塊業務邏輯無法融入任何現有聚合,而聚合又無法通過重新設計適應操作時,就需要考慮使用領域服務。下圖是領域服務文件夾:
在該項目與“Microsoft.NET企業級應用架構設計第二版”書中描述的領域服務職責不完全一樣,來看一下:
(1) 項目中,領域服務只是用來執行領域業務邏輯,包括了訂單服務OrderService和購物車服務BasketService。書中描述是可能跨多個實體。當一塊業務邏輯無法融入任何現有聚合。
總的來說,eShopOnWeb項目雖然沒有完全遵循領域層中,成員職責描述,但可以理解是在代碼上簡化了領域層的復雜性。
? (3) 倉儲
倉儲是協調領域模型和數據映射層的組件。倉儲是領域服務中最常見類型,它負責持久化。倉儲接口的實現屬于基礎設施層。倉儲通常基于一個IRepository接口。 下面看下項目定義的倉儲接口。
/// <summary>/// T是領域實體,是BaseEntity類型的實體
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IAsyncRepository<T> where T : BaseEntity
{
Task<T> GetByIdAsync(int id);
Task<IReadOnlyList<T>> ListAllAsync();
//使用領域規則查詢
Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec);
Task<T> AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
//使用領域規則查詢
Task<int> CountAsync(ISpecification<T> spec);
}
(4) 領域規則
在倉儲設計查詢接口時,可能還會用到領域規則。 在倉儲中一般都是定義固定的查詢接口,如上面倉儲的IAsyncRepository所示。而復雜的查詢條件可能需要用到領域規則。在本項目中通過強大Linq 表達式樹Expression?來實現動態查詢。
? 最后Interfaces文件夾中定義的接口,都由基礎設施層來實現。如:
???????? IAppLogger日志接口
???????? IEmailSender郵件接口
??????? ? IAsyncRepository倉儲接口
3.Infrastructure層
基礎設施層Infrastructure依賴于ApplicationCore,這遵循依賴倒置原則(DIP),Infrastructure中代碼實現了ApplicationCore中定義的接口(Interfaces文件夾)。該層沒有太多要講的,功能主要包括:使用EF Core進行數據訪問、Identity、日志、郵件發送。與Equinox項目的基礎設施層差不多,區別多了領域規則。
? ? ? ?領域規則SpecificationEvaluator.cs類用來構建查詢表達式(Linq expression),該類返回IQueryable<T>類型。IQueryable接口并不負責查詢的實際執行,它所做的只是描述要執行的查詢。
參考資料
Microsoft.NET企業級應用架構設計 第二版
原文地址:https://www.cnblogs.com/MrHSR/p/10869911.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的结合eShopOnWeb全面认识领域模型架构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ML.NET机器学习、API容器化与Az
- 下一篇: 微软开源Bing搜索背后的关键算法