實現領域驅動設計 - 使用ABP框架 - 創建實體

来源:https://www.cnblogs.com/broadm/archive/2022/06/24/16407775.html
-Advertisement-
Play Games

用例演示 - 創建實體 本節將演示一些示例用例並討論可選場景。 創建實體 從實體/聚合根類創建對象是實體生命周期的第一步。聚合/聚合根規則和最佳實踐部分 建議為Entity類創建一個主構造函數,以保證創建一個有效的實體。因此,無論何時我們需要創建實體的實例,我們都應該使用那個構造函數 參見下麵的問題 ...


用例演示 - 創建實體

本節將演示一些示例用例並討論可選場景。

創建實體

從實體/聚合根類創建對象是實體生命周期的第一步。聚合/聚合根規則和最佳實踐部分 建議為Entity類創建一個主構造函數,以保證創建一個有效的實體。因此,無論何時我們需要創建實體的實例,我們都應該使用那個構造函數

參見下麵的問題聚合根類:

public class Issue : AggregateRoot<Guid>
{
    public Guid RepositoryId { get; private set; }
    public string Title { get; private set; }
    public string Text { get; set; }
    public Guid? AssignedUserId { get; private set; }
    
    public Issue(
        Guid id, 
        Guid repositoryId,
        string title,
        string text = null
    ) : base(id)
    {
        RepositoryId = repositoryId;
        Title = Check.NotNullOrWhiteSpace(title, nameof(title));
        Text = text; // 允許空值
    }

    private Issue() { //為ORM保留的空構造函數 }

    public void SetTitle(string title)
    {
        Title = Check.NotNullOrWhiteSpace(title, nameof(title));
    }
}
  • 該類保證通過其構造函數創建有效的實體

  • 如果你需要更改標題,你需要使用 SetTitle 方法保證標題在一個有效狀態

  • 如果您想將這個問題分配給用戶,您需要使用 IssueManager (它在分配之前實現了一些業務規則, 請參閱我之前關於 領域服務 的文章)。

  • Text 屬性有一個公共setter,因為它也接受null值,並且這個示例沒有任何驗證規則。它在構造函數中也是可選的

讓我們看看用於創建問題的Application Service方法:

public class IssueAppService : ApplicationService, IIssueAppService
{
    //省略了Repository和DomainService的依賴註入

    [Authorize]
    public async Task<IssueDto> CreateAsync(IssueCreationDto input)
    {
        //創建一個有效的問題實體
        var issue = new Issue(
            GuidGenerator.Create(),
            input.RepositoryId,
            input.Title,
            input.Text
        );

        //如果傳入了被分配人,則把該問題法分配給這個用戶
        if(input.AssignedUserId.HasValue)
        {
            var user = await _userRepository.GetAsync(input.AssignedUserId.Value);
            await _issueManager.AssignToAsync(issue, user);
        }

        // 把問題實體保存到資料庫
        await _issueRepository.InsertAsync(issue);

        //返回表示這個新的問題的DTO
        return ObjectMapper.Map<Issue, IssueDto>(issue);
    }
}

CreateAsync 方法:

  • 使用 Issue 構造函數創建有效的問題。它使用 IGuidGenerator 服務傳遞Id。這裡不使用自動對象映射
  • 如果客戶端希望在對象創建時將這個問題分配給用戶,它會使用IssueManager 來完成,允許 IssueManager 在分配之前執行必要的檢查。
  • 保存實體到資料庫
  • 最後使用 IObjectMapper 返回一個 IssueDto ,該 IssueDto 是通過映射從新的 Issue 實體自動創建的

使用領域規則創建實體

上述示例, Issue 沒有關於實體創建的業務規則,除了在構造函數中進行一些形式的驗證。但是,在某些情況下,實體創建應該檢查一些額外的業務規則

例如,假設您不希望在完全相同的標題已經存在問題的情況下創建問題。在哪裡實現這個規則? 在 Application Service 中實現此規則是不合適的,因為它是一個應該始終檢查的 核心業務(領域)規則

該規則應該在 領域服務 (在本例中是 IssueManager )中實現。因此,我們需要強制應用層總是使用 IssueManager 來創建一個新的 Issue

首先,我們可以將 Issue 構造函數設置為 internal ,而不是 public:

public class Issue : AggregateRoot<Guid>
{
    internal Issue(
        Guid id, 
        Guid repositoryId,
        string title,
        string text = null
    ) : base(id)
    {
        //...
    }
}

這阻止了應用服務直接使用構造函數,所以它們將使用 IssueManager 。然後我們可以在 IssueManager 中添加一個 CreateAsync 方法:

public class IssueManager : DomainService
{
    //省略了依賴註入

    public async Task<IssueDto> CreateAsync(
        Guid repositoryId,
        string title,
        string text = null
    )
    {
        //如果存在相同標題的問題,直接拋錯
        if(await _issueRepository.AnyAsync(i => i.Title == title))
        {
            throw new BusinessException("IssueTracking:IssueWithSameTitleExists");
        }

        //創建一個有效的問題實體
        return new Issue(
            GuidGenerator.Create(),
            repositoryId,
            title,
            text
        );
    }
}
  • CreateAsync 方法檢查相同標題是否已經存在問題,併在這種情況下拋出業務異常
  • 如果沒有重覆,則創建並返回一個新的Issue

為了使用上述方法,IssueAppService 被修改如下:

public class IssueAppService : ApplicationService, IIssueAppService
{
    //省略了依賴註入

    public async Task<IssueDto> CreateAsync(IssueCreationDto input)
    {
        //★修改為通過領域服務創建有效的問題實體, 而不是直接new
        var issue = await _issueManager.CreateAsync(
            GuidGenerator.Create(),
            input.RepositoryId,
            input.Title,
            input.Text
        );

        //如果傳入了被分配人,則把該問題法分配給這個用戶
        if(input.AssignedUserId.HasValue)
        {
            var user = await _userRepository.GetAsync(input.AssignedUserId.Value);
            await _issueManager.AssignToAsync(issue, user);
        }

        // 把問題實體保存到資料庫
        await _issueRepository.InsertAsync(issue);

        //返回表示這個新的問題的DTO
        return ObjectMapper.Map<Issue, IssueDto>(issue);
    }
}

討論:為什麼問題沒有在 IssueManager 中保存到資料庫?

你可能會問 “為什麼 IssueManager 不把問題保存到資料庫中?” 我們認為這是應用服務的責任

因為,在保存問題對象之前,應用程式服務可能需要對其進行額外的更改/操作。如果領域服務保存它,則保存操作將重覆

  • 兩次資料庫往返會導致性能損失
  • 需要顯式的資料庫事務來包含這兩個操作
  • 如果由於業務規則的原因,其他操作取消了實體創建,則應該在資料庫中回滾事務

當你檢查 IssueAppService 時,你會看到在 IssueManager.CreateAsync 中不保存 Issue 到資料庫的好處。否則,我們將需要執行一次插入(在 IssueManager 中)和一次更新(在分配問題之後)

討論:為什麼不在應用程式服務中實現重覆標題檢查?

我們可以簡單地說 “因為它是一個核心領域邏輯,應該在領域層中實現”。然而,這帶來了一個新的問題: “您如何判斷它是核心領域邏輯,而不是應用程式邏輯?” (稍後我們將詳細討論其中的差異)

對於這個例子,一個簡單的問題可以幫助我們做出決定: “如果我們有另一種方法(用例)來創建一個問題,我們是否仍然應用相同的規則?” 你可能會想 “為什麼我們有第二種製造問題的方式?” 然而,在現實生活中,你有:

  • 應用程式的最終用戶可能會在應用程式的標準UI中創建問題(比如在github的網頁端創建問題)
  • 您可能有第二個後臺應用程式,由您自己的員工使用,您可能希望提供一種創建問題的方法(在本例中可能使用不同的授權規則)
  • 您可能有一個對第三方客戶端開放的HTTP API,他們會創建問題。
  • 您可能有一個 background worker service,如果它檢測到一些故障,它會做一些事情並創建問題。這樣,它將在沒有任何用戶交互的情況下(可能沒有任何標準的授權檢查)創建問題。
  • 您甚至可以在UI上設置一個按鈕,將某些內容 (例如,討論) 轉換為問題

綜上所述,不同的應用程式始終遵循這樣的規則:新問題的標題不能與任何現有問題的標題相同!他們與應用層無關! 這就是為什麼該邏輯是核心領域邏輯,應該位於領域層中,而不應該在應用程式服務中實現為重覆的代碼。


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

-Advertisement-
Play Games
更多相關文章
  • 1.路徑處理 1.找模塊:sys.path import sys print(sys.path) - 1.理解 - 1.是python去查找包或模塊 - 2.項目開始根目錄,python內置的目錄 - 3.雖然說python的安裝目錄下也可以存放我們寫的模塊,但是不建議(太多了,不大好找) - 4. ...
  • 本篇內容將在上一篇已有的內容基礎上,進一步的聊一下項目中使用JPA的一些高階複雜場景的實踐指導,覆蓋了主要核心的JPA使用場景,可以讓你在需求開發的時候對JPA的使用更加的游刃有餘。 ...
  • 前言 Steam是由美國電子游戲商Valve於2003年9月12日推出的數字發行平臺,被認為是電腦游戲界最大的數位發行平臺之一,Steam平臺是全球最大的綜合性數字發行平臺之一。玩家可以在該平臺購買、下載、討論、上傳和分享游戲和軟體。 而每周的steam會開啟了一輪特惠,可以讓游戲打折,而玩家就會 ...
  • Hi,大家好,我是Mic 一個工作5年的粉絲找到我。 他說: “Mic老師,你要是能回答出這個問題,我就佩服你” 我當場就懵了,現在打賭都這麼隨意了嗎? 我問他問題是什麼,他說“Kafka如何避免重覆消費的問題!” 下麵看看普通人和高手的回答! 普通人: Kafka怎麼避免重覆消費就是我們可以通過 ...
  • 前言 今天給大家分享一下我自己寫的筆記,純純的都是乾貨,關於字好像也能看。這是我學python整理出來的一些資料,希望對大家 有用。想要更多的資料那就的給一個關註了… python學習交流Q群:903971231### #導入Counter from collections import Count ...
  • Zookeeper3.7源碼剖析 能力目標 掌握Zookeeper中Session的管理機制 能基於Client進行Debug測試Session創建/刷新操作 能搭建Zookeeper集群源碼配置 掌握集群環境下Leader選舉啟動過程 能說出Zookeeper選舉過程中的概念 能說出Zookeep ...
  • 表弟大學快畢業了,學了一個學期Python居然還不會寫學生管理系統,真的給我丟臉啊,教他又不肯學,還讓我直接給他寫,我真想兩巴掌上去,最終還是寫了給他,誰讓他是我表弟呢,關鍵時候還是得幫他一把! 寫完了放在那也是放著,所以今天分享給大家吧! 話不多說,咱們直接開始吧! 代碼解析 一、登錄頁面 1、定 ...
  • 領域邏輯 & 應用邏輯 如前所述,領域驅動設計中的業務邏輯分為兩部分(層):領域邏輯和應用邏輯: 領域邏輯由系統的核心領域規則組成,應用邏輯實現應用特定的用例 雖然定義很明確,但實現起來可能並不容易。您可能無法決定哪些代碼應該位於應用程式層,哪些代碼應該位於領域層。本節試圖解釋其中的差異 多個應用程 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...