[Abp vNext 源碼分析] - 6. DDD 的應用層支持 (應用服務)

来源:https://www.cnblogs.com/myzony/archive/2019/07/25/11246623.html
-Advertisement-
Play Games

一、簡要介紹 ABP vNext 針對於應用服務層,為我們單獨設計了一個模塊進行實現,即 Volo.Abp.Ddd.Application 模塊。 PS:最近博主也是在惡補 DDD 相關的知識,這裡推薦大家看一下 "ThoughtWorks" 的 DDD 相關文章。 關於 DDD 相關的著作,我這兒 ...


一、簡要介紹

ABP vNext 針對於應用服務層,為我們單獨設計了一個模塊進行實現,即 Volo.Abp.Ddd.Application 模塊。

PS:最近博主也是在惡補 DDD 相關的知識,這裡推薦大家看一下 ThoughtWorks 的 DDD 相關文章。

關於 DDD 相關的著作,我這兒還是推薦經典的那三本《領域驅動設計:軟體核心複雜性應對之道》、《實現領域驅動設計》、《領域驅動設計精粹》

DDD 的學習整體來說是比較枯燥的,而且偏理論化的知識。所以需要結合大量實例來看,反覆對照書中的概念加深理解。不僅要看別人的實例,自己也要嘗試運用 DDD 的戰略方法和戰術方法進行設計。

應用服務層在 DDD 分層架構裡面是最頂層的,一般與前端(展示層)打交道的都是應用服務層。常規的開發人員,如果沒有遵循 DDD 理論來進行開發的話,應用服務層是十分臃腫的,裡面全是業務邏輯。而領域層裡面則是空無一物,全是貧血的領域模型對象。這種模式被稱之為 貧血領域模型模式,這是一個 反模式

這裡我就不再贅述應用服務層與 DDD 之間的關係了,在這裡你可以看作它是一個 API 介面實現類,你所有對外開放的介面都是通過應用服務層暴露的,介面的方法應該與用例相對應。

二、源碼分析

應用服務層模塊裡面比較簡單,只有兩個文件夾,分別存放了數據傳輸模型(Dtos)和應用服務基類定義(Services)。

2.1 啟動模塊

首先我們還是按照之前的順序,看一個模塊先看他的模塊類。這裡我們先看一下 AbpDddApplicationModule 的代碼。

[DependsOn(
    typeof(AbpDddDomainModule),
    typeof(AbpSecurityModule),
    typeof(AbpObjectMappingModule),
    typeof(AbpValidationModule),
    typeof(AbpAuthorizationModule),
    typeof(AbpHttpAbstractionsModule),
    typeof(AbpSettingsModule),
    typeof(AbpFeaturesModule)
    )]
    // 不要看上面依賴這麼多模塊,主要是因為基類會用到很多基礎組件。
public class AbpDddApplicationModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        // 配置介面類型。
        Configure<ApiDescriptionModelOptions>(options =>
        {
            options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService));
            options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService));
            options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled));
        });
    }
}

可以看到,在上述代碼裡面,只做了一件事情,就是調用 ApiDescriptionModelOptions ,往裡面添加了 IRemoteServiceIApplicationServiceIUnitOfWOrkEnabled 三種介面類型。添加了三種類型之後,ABP vNext 根據應用服務類創建控制器時,就會從這個 IgnoredInterfaces 判斷哪些類型不被忽略 (即只會自動註冊實現了三種介面的類型成為控制器)。

2.2 應用服務基類

ABP vNext 提供了標準基類 ApplicationService 和簡單 Crud 基類 CrudAppService 給我們使用,前者只是繼承了 IApplicationService 介面,並提供了基本組件的簡單基類。而後者則是定義了 Crud 操作所需要的所有 API 方法,你只需要繼承這個基類對象,填充相應的泛型參數,就可以快速實現一個 Crud 介面。

2.2.1 簡單基類

簡單基類裡面我們首先需要註意的是它實現的介面,你可以發現 ApplicationService 實現了諸多介面,不過這些介面更多的是類似於標識介面。

public abstract class ApplicationService :
    IApplicationService,
    IAvoidDuplicateCrossCuttingConcerns,
    IValidationEnabled,
    IUnitOfWorkEnabled,
    IAuditingEnabled,
    ITransientDependency
{
    // ... 其他代碼
}

所有應用服務都必須繼承 IApplicationService,這個是肯定的,不然 ABP vNext 不會為我們生成需要的控制器。

其次是 IAvoidDuplicateCrossCuttingConcerns 介面,這個介面最早可以追溯到老版本 ABP 框架裡面。它的主要作用是防止攔截器進行重覆執行。

public interface IAvoidDuplicateCrossCuttingConcerns
{
    List<string> AppliedCrossCuttingConcerns { get; }
}

例如調用購買這個 API 介面,首先會進入 ASP.NET Core 的審計日誌 Filter,在 Filter 裡面會將這個 API 介面歸屬的類型的 List 容器(介面裡面定義的 List )裡面寫入一條記錄,說明已經通過審計日誌過濾器記錄了。

寫了審計日誌之後,又會進入審計日誌攔截器,這個時候攔截器就會對指定的類型進行判斷,看是否已經被執行過了,因為這個類型的 List 容器有了之前過濾器的記錄,所以不會重覆執行。

public override void Intercept(IAbpMethodInvocation invocation)
{
    if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction))
    {
        invocation.Proceed();
        return;
    }

    // ... 審計日誌記錄。
}

protected virtual bool ShouldIntercept(
    IAbpMethodInvocation invocation, 
    out AuditLogInfo auditLog, 
    out AuditLogActionInfo auditLogAction)
{
    // 判斷實例的 List 容器裡面,是否寫入了 AbpCrossCuttingConcerns.Auditing。
    if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing))
    {
        return false;
    }
    
    // ... 其他代碼

    return true;
}

剩餘的 IValidationEnabledIUnitOfWorkEnabledIAuditingEnabledITransientDependency 介面類似於一個啟用標識,只要類型繼承了該介面,就會執行一些特殊的操作。

回到之前的簡單基類裡面,ABP vNext 為我們註入了大量基礎設施,例如獲取當前用戶的 ICurrentUser 組件,獲取當前租戶的 ICurrentTenant 組件,還有日誌組件等。

除了基礎組件,ABP vNext 在簡單基類裡面還提供了一個許可權檢測方法,用戶檢測當前用戶是否具備某些許可權。

protected virtual async Task CheckPolicyAsync([CanBeNull] string policyName)
{
    if (string.IsNullOrEmpty(policyName))
    {
        return;
    }

    await AuthorizationService.CheckAsync(policyName);
}

在不具備許可權的時候,ABP vNext 會拋出 AbpAuthorizationException 異常。

2.2.2 Crud 基類

Crud 基類可以極大減少對於某些簡單對象的代碼編寫,例如我有個客戶管理介面,只需要簡單地增刪改查操作。那麼我就可以直接繼承自 Crud 基類,給它填寫和是的泛型參數之後,ABP vNext 就會為我們生成帶有增刪改查操作的應用服務對象。

這個 Crud 基類擁有多個泛型定義與實現,除了真正的實現以外,其他的都是簡單的調用基類方法而已。我們直接進入主題,看一下類型簽名為 public abstract class CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> 的基類。

public abstract class CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
   : ApplicationService,
    ICrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
    where TEntity : class, IEntity<TKey>
    where TGetOutputDto : IEntityDto<TKey>
    where TGetListOutputDto : IEntityDto<TKey>
{
    public virtual async Task<TGetOutputDto> GetAsync(TKey id)
    {
        // 具體代碼。
    }
    
    public virtual async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input)
    {
        // 具體代碼。
    }
    
    public virtual async Task<TGetOutputDto> CreateAsync(TCreateInput input)
    {
        // 具體代碼。
    }
    
    public virtual async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
    {
        // 具體代碼。
    }
    
    public virtual async Task DeleteAsync(TKey id)
    {
        // 具體代碼。
    }
}

從上述代碼可以看到基類根據傳入的泛型參數,將會為我們實現常規的增刪改查邏輯。我們也可以隨時重寫這些方法,來達到一些個性化的操作。

ABP vNext 抽象了公用介面以外,在內部還編寫了諸如 MapToEntity()MapToEntity() 等內部共用方法,這裡就不再詳細贅述,這些方法都是 protected 修飾的,你也可以隨時重寫來達到自己的目的。

2.3 數據傳輸對象

一般來說,應用服務層返回給展示層的數據肯定是某個實體對象的部分屬性,或者是多個聚合的整體,這個時候就需要 DTO 來幫我們處理應用服務層與外部的數據交換了。

ABP vNext 在應用服務模塊定義了常用的一些 DTO 對象,例如實體 DTO 和分頁查詢 DTO,關於這些 DTO 你只需將其看作一個數據容器即可,不需要太多關註,這裡也沒有太多要講的。

三、總結

ABP vNext 提供的應用服務層模塊還是比較簡單的,裡面主要是針對應用服務基類進行了預定義。方便我們開發人員進行業務開發,而不需要自己實現這些繁雜的基類。

在 DDD 當中,應用服務是表達 用戶用例用戶故事 的主要手段,應用服務只是通過領域對象/領域服務來表達需求用例的一個組件。不要將業務邏輯泄漏到應用服務當中,這種設計最終會導致貧血領域模型。

四、點擊我跳轉到文章目錄


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • List集合類和ArrayList集合類都是C#語言中用於存儲集合數據的集合類,兩者都可靈活的插入、刪除以及訪問元素等等。但List集合和ArrayList集合的差別還是挺大的,首先List集合類是泛型集合,List集合中存儲的數據的類型都是確定的。ArrayList集合類為非泛型類集合,集合中存儲 ...
  • 在C#的集合操作過程中,我們一般常用的集合類為List集合,List集合是一種強類型的泛型集合,其實還有一個ArrayList集合類,ArrayList集合類則非泛型類的集合,並且ArrayList集合類不是強類型,任何類型的數據往ArrayList集合中新增都可以,所以在進行集合內部元素轉換操作的 ...
  • 在C#中的Datatable類中,Clone方法和Copy方法都可以用來複制當前的DataTable對象,但DataTable類中的Clone方法和Copy方法還是有區別的,Clone方法只複製結構信息,包括所有 DataTable 架構和約束,但Copy方法除了複製結構信息之外,還複製DataTa ...
  • 在C#中的Datatable類中,可以使用DataTable類的Merge方法對兩個相同結構的DataTable對象進行求並集運算,將兩個DataTable對象的數據行合併到其中一個DataTable變數中,或者說往其中一個DataTable對象中寫入另一個DataTable對象的所有數據行。下列例 ...
  • 在C#中的Datatable數據變數的操作過程中,有時候需要知道某一個列名在DataTable中的索引位置信息,此時可以通過DataTable變數的Columns屬性來獲取到所有的列信息,然後通過Columns屬性中的IndexOf方法來獲取指定列名的索引位置,IndexOf方法的參數為列名。即通過 ...
  • C#中的Datatable數據變數的操作過程中,可以通過DataTable的Copy方法快速複製當前的DataTable變數到新對象中,複製數據包含當前DataTable的結構信息如列名,同時也包含當前表格中的所有數據信息,通過Copy方法複製DataTable對象方便快捷,無需迴圈遍歷原有的Dat ...
  • 在C#中的Datatable數據變數的操作過程中,有時候需要判斷DataTable中是否存在某個列名,此時可以通過DataTable對象的Columns列對象集合屬性下的Contains方法來進行判斷,方便快捷,只需要傳入需要匹配的列的名稱,即可返回DataTable變數中是否存在該列名。 首先給出 ...
  • Hello World //列印語句 Console.WriteLine("Hello World"); //暫停 Console.ReadKey(); 數據類型 1.值類型 byte,char,short,int,long,bool,decimal,float,double,sbyte,uint, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...