基於ABP實現DDD--領域邏輯和應用邏輯

2022-07-25 06:01:01

  本文主要介紹了多應用層的問題,包括原因和實現。通過理解介紹瞭如何區分領域邏輯和應用邏輯,哪些是正確的實踐,哪些是不推薦的或者錯誤的實踐。

一.多應用層的問題

1.多應用層介紹

  不知道你們是否會遇到一種情況,通過ABP構建了一個後端的API專案,剛開始是為Web端專案(比如,Vue)提供後端介面服務的,隨著專案的發展和業務的複雜,增加了行動端的App,或者公眾號、小程式等,這樣不僅要為Web端提供API服務,而且還需要為行動端的App,或者公眾號、小程式等提供API服務。這個場景就是多應用層的問題。也就是說你現在需要構建一個有多個應用程式的系統了:

  • Web應用程式:比如使用的ASP.NET Core MVC技術,主要用來給使用者展示產品,遊客模式是可以檢視產品的,並不需要登入和驗證。
  • 後端管理應用程式:比如前端是Vue,後端的ASP.NET Core API,主要用來對產品進行增刪改等操作。
  • 移動應用程式:比如前端是uni-app,後端是ASP.NET Core API,通過REST介面和後端通訊。

2.多應用層例子

  由於業務的複雜性,每個應用系統都有自己的不同應用服務方法,不同的輸入和輸出DTO,不用的認證和授權規則等,所以如果把所有的業務邏輯都融入到一個應用系統中,就會讓系統更難開發、維護和測試,並導致潛在的Bug風險。因此,為每個應用程式建立單獨的應用層,並且它們都是用單個領域層來共用核心領域邏輯。為了更加具體的說明,為每種應用程式型別建立不同的.csproj專案:

  • 後端管理應用程式:IssueTracker.Admin.Application和IssueTracker.Admin.Application.Contracts專案
  • Web應用程式:IssueTracker.Public.Application和IssueTracker.Public.Application.Contracts專案
  • 移動應用程式:IssueTracker.Mobile.Application和IssueTracker.Mobile.Application.Contracts專案

二.如何區分領域邏輯和應用邏輯

通常,DDD中的業務邏輯包括領域邏輯和應用邏輯。領域邏輯由系統的核心領域規則組成,而應用程式邏輯實現特定於應用程式的用例。話雖這樣說,但是並不容易區分什麼是領域邏輯和應用邏輯。

1.在領域服務層中建立組織Organization

下面通過在領域服務中建立Organization這個例子,來儘可能簡要說明:

public class OrganizationManager:DomainService
{
    private readonly IRepository<Organization> _organizationRepository; //Organization的倉儲
    private readonly ICurrentUser _currentUser; //當前使用者
    private readonly IAuthorizationService _authorizationService; //Authorization的服務
    private readonly IEmailSender _emailSender; //郵件傳送服務
    
    // 公共建構函式,依賴注入
    public OrganizationManager(IRepository<Organization> organizationRepository, ICurrentUser currentUser, IAuthorizationService authorizationService, IEmailSender emailSender)
    {
        _organizationRepository=organizationRepository;
        _currentUser=currentUser;
        _authorizationService=authorizationService;
        _emailSender=emailSender;
    }
    
    // 建立一個新的組織
    public async Task<Organization> CreateAsync(string name)
    {
        // 如果組織存在同名,那麼丟擲異常[正確]
        if(await _organizationRepository.AnyAsync(x=>x.Name==name))
        {
            throw new BusinessException("IssueTracking:DuplicateOrganizationName");
        }
        
        // 檢查是否擁有建立的許可權[錯誤]
        await _authorizationService.CheckAsync("OrganizationCreationPermission");
        
        // 記錄⽇志[錯誤]
        Logger.LogDebug($"Creating organization {name} by {_currentUser.UserName}");
        
        // 建立一個新的組織
        var organization = new Organization();
        
        // 傳送郵件進行提醒[錯誤]
        await _emailSender.SendAsync("[email protected]", "新組織", "新組織名稱:"+name);
        
        // 返回一個組織範例
        return organization;
    }
}
  • 領域服務不做許可權驗證,許可權驗證放在應用層來做
  • 使用者概念是應用層或展示層的相關概念,記錄紀錄檔不應該包含當前使用者的使用者名稱
  • 建立一個新的組織,並行送郵件,這個業務邏輯也應該放在應用層

2.在應用層中使用領域服務建立組織Organization

public class OrganizationAppService:ApplicationService
{
    private readonly OrganizationManager _organizationManager; //組織的領域服務
    private readonly IPaymentService _paymentService; //支付服務
    private readonly IEmailSender _emailSender; //郵件服務
    
    // 公共建構函式,依賴注入
    public OrganizaitonAppService(OrganizationManager organizationManager, IPaymentService paymentService, IEmailSender emailSender)
    {
        _organizationManager=organizationManager;
        _paymentService=paymentService;
        _emailSender=emailSender;
    }
    
    // 建立組織
    [UnitOfWork][正確] //工作單元,用於提交事務
    [Authorize("OrganizationCreationPermission")][正確]
    public async Task<Organization> CreateAsync(CreateOrganizationDto input)
    {
        // ⽀付組織的費⽤[正確]
        await _paymentService.ChargeAsync(CurrentUser.Id, GetOrganizationPrice());
        
        // 通過領域服務,建立一個新的組織範例
        var organization = await _organizationManager.CreateAsync(input.Name);
        
        // 儲存和更新組織到資料庫中[正確]
        await _organizationManager.InsertAsync(organization);
        
        // 傳送提醒郵件[正確]
        await _emailSender.SendAsync("[email protected]", "新組織", "新組織名稱:"+name);
        
        //返回範例[錯誤]
        return organization;
    }
    private double GetOrganizationPrice()
    {
        return 42;//Gets form somewhere...
    }
}

應用服務層的輸入和輸出引數都是DTO,不能返回實體。至於為什麼不將支付放在領域服務中,只能說業務重要也不一定放在領域服務中,詳細原因說明參考[1]。

參考文獻:
[1]基於ABP Framework實現領域驅動設計:https://url39.ctfile.com/f/2501739-616007877-f3e258?p=2096 (存取密碼: 2096)