1. 程式人生 > >C#進階系列——DDD領域驅動設計初探(一):聚合

C#進階系列——DDD領域驅動設計初探(一):聚合

前言:又有差不多半個月沒寫點什麼了,感覺這樣很對不起自己似的。今天看到一篇博文裡面寫道:越是忙人越有時間寫部落格。呵呵,似乎有點道理,博主為了證明自己也是忙人,這不就來學習下DDD這麼一個聽上去高大上的東西。前面介紹了下MEF和AOP的相關知識,後面打算分享Automapper、倉儲模式、WCF等東西的,可是每次準備動手寫點什麼的時候,就被要寫的Demo難住了,比如倉儲模式,使用過它的朋友應該知道,如果你的專案不是按照DDD的架構而引入倉儲的設計,那麼會讓它變得很“雞肋”,用不好就會十分痛苦,之前看過這篇 部落格園的大牛們,被你們害慘了,Entity Framework從來都不需要去寫Repository設計模式

 文章的朋友應該還記得,不止是該文章的作者,很多園友在評論裡面也提到了使用它的不爽。博主的專案中也遇到類似的問題,雖然引入了倉儲模式,但是由於沒有架構好,把倉儲的介面和實現統一放在了資料訪問層,導致到後面程式碼越寫越難維護,完全感覺不到倉儲帶來的好處。所以博主覺得單純分享倉儲模式很容易使讀者陷入“為了模式而模式”的誤區,再加上最近一段時間在看《領域驅動設計:軟體核心複雜性應對之道.Eric.Eva》這本書和部落格園大牛dax.net的DDD系列文章,所以打算分享一個Demo來說明倉儲模式、Automapper、WCF等知識點。

DDD領域驅動設計初探系列文章:

一、領域驅動設計基本概念

根據《領域驅動設計:軟體核心複雜性應對之道.Eric.Eva》書中的觀點,領域模型是軟體專案的公共語言的核心,是領域專家和開發人員共同遵守的通用語言規則,那麼在DDD裡面,建模的重要性不用多說,所以要想更好理解領域驅動設計,理解領域模型的劃分和建立就變得相當必要。首先來看看DDD裡面幾個比較重要的概念:

1、領域模型:領域模型與資料模型不同,它表述的是領域中各個類及其之間的關係。從領域驅動設計的角度看,資料庫只不過是儲存實體的一個外部機制,是屬於技術層面的東西。資料模型主要用於描述領域模型物件的持久化方式,應該是先有領域模型,才有資料模型,領域模型需要通過某種對映而產生相應的資料模型,從這點來說,最新的EF的Code First就是一個很好的體現。領域模型物件分為實體、值物件和服務。

2、實體:在領域驅動設計裡面,實體是模型中需要區分個體的物件。這裡的實體和EntityFramework裡面的實體不是一個概念,EF的實體為資料實體,不包含物件的行為。就博主的理解,DDD概念裡面的實體就是包括實體資料(EF的Model)和行為的結合體

3、值物件:通過物件屬性值來識別的物件,它將多個相關屬性組合為一個概念整體。相比實體而言,值物件僅僅是比實體少了一個標識。值物件的設計比較存在爭議,我們暫且記住值物件和實體的區別:(1)實體擁有唯一標識,而值物件沒有;(2)實體允許變化,而值物件不允許變化;(3)判斷兩個實體相等的方法是判斷實體的標識相等,而判斷兩個值物件相等的標準是值物件內部所有屬性值相等;

4、聚合(以及聚合根):聚合表示一組領域物件(包括實體和值物件),用來表述一個完整的領域概念。而每個聚合都有一個根實體,這個根實體又叫做聚合根。舉個簡單的例子,一個電腦包含硬碟、CPU、記憶體條等,這一個組合就是一個聚合,而電腦就是這個組合的聚合根。博主覺得關於聚合的劃分學問還是挺大的,需要在實踐中慢慢積累。同一個實體,在不同的聚合中,它可能是聚合根,也可能不是,需要根據實際的業務決定。聚合根是聚合所表述的領域概念的主體,外部物件需要訪問聚合內的實體時,只能通過聚合根進行訪問,而不能直接訪問

5、領域服務:博主的理解,領域模型主張富領域模式,也就是說把領域邏輯儘量寫在領域實體裡面,也就是常說的“充血模式”,而對於業務邏輯,最好是以服務的形式提供。至於領域邏輯和業務邏輯的界定,這個要根據實際情況來定。總之,領域服務是用來處理那些領域模型裡面不好定義或者某些可變邏輯的的時候才會用到。待驗證!

6、工廠、倉儲等概念留在Demo裡面說明。

二、領域驅動設計開始之旅

1、專案分層

領域驅動設計將軟體系統分為四層:基礎結構層、領域層、應用層和表現層。來看看書中的分層:

其實在dax.net的系列中這張圖更能說明這種架構

2、專案架構

博主打算用許可權系統的案例說明的領域驅動設計的專案架構。專案嚴格按照表現層、應用層、領域層、基礎設施層來劃分。

表現層:MVC的Web專案,負責UI呈現。

應用層:WCF服務,負責協調領域層的呼叫,向UI層提供需要的介面。

領域層:定義領域實體和領域邏輯。

基礎設施層:一些通用的技術,比如AOP、MEF注入、通用的工具類、DTO模型層,這裡為什麼要有一個DTO模型層,DTO是用於UI展現用的純資料Model,它不包含實體行為,是一種貧血的模型。

整個專案的呼叫方式嚴格按照DDD設計來進行,UI層通過WCF服務呼叫應用層的WCF介面,WCF服務通過倉儲呼叫領域層裡面的介面,基礎設施層貫穿其他各層,在需要的專案中都可以引用基礎設施層裡面的內庫。

3、程式碼示例

接下來,博主就根據自己的理解,從零開始使用這種架構寫一個簡單的許可權管理系統。由於是領域驅動設計,所以,文章的重點會放在領域層,專案使用了EF的Model First來進行,先設計實體,後生成資料庫。

3.1 首先來看看錶結構

 

根據博友要求,這裡說明一下表之間的對映關係:

1表示TB_DEPARTMENT表的主鍵DEPARTMENT_ID作為TB_USERS表的外來鍵;

2表示TB_USERS表的主鍵USER_ID作為TB_USERROLE表的外來鍵;

3表示TB_ROLE表的主鍵ROLE_ID作為TB_USERROLE表的外來鍵;

4表示TB_ROLE表的主鍵ROLE_ID作為TB_MENUROLE表的外來鍵

5表示TB_MENU表的主鍵MENU_ID作為TB_MENUROLE表的外來鍵

首先建好對應的表實體,然後根據模型生成資料庫

將生成的sql語句執行後就可以得到對應的表結構。

3.2 聚合的劃分

在領域層裡面我們新建一個BaseModel,裡面有三個類

這三個類IEntity、IAggregateRoot、AggregateRoot分別定義了實體的介面、聚合根的介面、聚合根的抽象實現類。

    //用作泛型約束,表示繼承自該介面的為領域實體
    public interface IEntity
    {

    }
    /// <summary>
    /// 聚合根介面,用作泛型約束,約束領域實體為聚合根,表示實現了該介面的為聚合根例項,由於聚合根也是領域實體的一種,所以也要實現IEntity介面
    /// </summary>
    public interface IAggregateRoot:IEntity
    {

    }
   /// <summary>
    /// 聚合根的抽象實現類,定義聚合根的公共屬性和行為
    /// </summary>
    public abstract class AggregateRoot:IAggregateRoot
    {
        
    }

這裡定義介面的作用是定義實體和聚合根的泛型約束,抽象類用來定義聚合根的公共行為,目前為止,這些介面和類裡面都是空的,後面會根據專案的需求一步一步往裡面加入邏輯。

在EF裡面由edmx檔案會生成實體的屬性,前面說到領域模型主張充血模式,所以要在EF的實體model裡面加入實體的行為,為了不改變EF生成實體的程式碼,我們使用partial類來定義實體的行為。我們來看Model資料夾下面的程式碼

    public partial class TB_DEPARTMENT: AggregateRoot
    {
        public override string ToString()
        {
            return base.ToString();
        }
    }
    public partial class TB_MENU : AggregateRoot
    {

    }
    /// <summary>
    /// 由於不會直接操作此表,所以TB_MENUROLE實體不必作為聚合根,只是作為領域實體即可
    /// </summary>
    public partial class TB_MENUROLE:IEntity
    {
    }
    public partial class TB_ROLE:AggregateRoot
    {
    }
    public partial class TB_USERROLE:IEntity
    {
    }
    public partial class TB_USERS:AggregateRoot
    {
    }

我們看到,這些實體,只有TB_MENUROLE和TB_USERROLE不是聚合根,其他實體都是聚合根。我這裡大概劃分為4個聚合:

聚合1:TB_DEPARTMENT實體

聚合2:TB_MENU、TB_MENUROLE、TB_ROLE這3個為一個聚合,聚合根是TB_MENU。

聚合3:TB_USERS、TB_USERROLE、TB_DEPARTMENT、TB_ROLE這4個為一個聚合,聚合根是TB_USERS。

聚合4:TB_ROLE、TB_USERS、TB_USERROLE、TB_MENUROLE、TB_MENU這5個表為一個聚合,聚合根是TB_ROLE。

可能這樣分會有一定的問題,後續出現再慢慢糾正。

到這裡,聚合的劃分基本完了,至於為什麼要做這麼一些約束和設計,因為倉儲只能對聚合根做操作,下篇講倉儲的時候會詳細說明。

DDD博大精深,文中很多觀點為博主個人理解,可能不太成熟或者有誤,歡迎拍磚~~

相關推薦

C#系列——DDD領域驅動設計初探聚合

前言:又有差不多半個月沒寫點什麼了,感覺這樣很對不起自己似的。今天看到一篇博文裡面寫道:越是忙人越有時間寫部落格。呵呵,似乎有點道理,博主為了證明自己也是忙人,這不就來學習下DDD這麼一個聽上去高大上的東西。前面介紹了下MEF和AOP的相關知識,後面打算分享Automapper、倉儲模式、WCF等東西的,可是

C#系列——DDD領域驅動設計初探領域服務

前言:之前一直在搭建專案架構的程式碼,有點偏離我們的主題(DDD)了,這篇我們繼續來聊聊DDD裡面另一個比較重要的知識點:領域服務。關於領域服務的使用,書中也介紹得比較晦澀,在此就根據博主自己的理解談談這個知識點的使用。 DDD領域驅動設計初探系列文章: 一、領域服務的引入 在《領域驅動設計:軟體核

C#系列——DDD領域驅動設計初探WCF搭建

前言:前面三篇分享了下DDD裡面的兩個主要特性:聚合和倉儲。領域層的搭建基本完成,當然還涉及到領域事件和領域服務的部分,後面再專案搭建的過程中慢慢引入,博主的思路是先將整個架構走通,然後一步一步來新增相關元素,使架構慢慢變得豐滿。這篇打算分享下應用層的搭建。根據DDD的設計原則,應用層不包含任何領域邏輯,它主

C#系列——DDD領域驅動設計初探AutoMapper使用

前言:前篇搭建了下WCF的程式碼,就提到了DTO的概念,對於為什麼要有這麼一個DTO的物件,上章可能對於這點不太詳盡,在此不厭其煩再來提提它的作用: 從安全上面考慮,領域Model都帶有領域業務,讓Client端引用Domain Model就意味著Client端可以繞過應用層直接完成業務邏輯的呼叫,這樣

C#系列——DDD領域驅動設計初探倉儲Repository

前言:上篇介紹了下倉儲的程式碼架構示例以及簡單分析了倉儲了使用優勢。本章還是繼續來完善下倉儲的設計。上章說了,倉儲的最主要作用的分離領域層和具體的技術架構,使得領域層更加專注領域邏輯。那麼涉及到具體的實現的時候我們應該怎麼做呢,本章就來說說倉儲裡面具體細節方便的知識。 DDD領域驅動設計初探系列文章:

C#系列——DDD領域驅動設計初探倉儲Repository

前言:上篇介紹了DDD設計Demo裡面的聚合劃分以及實體和聚合根的設計,這章繼續來說說DDD裡面最具爭議的話題之一的倉儲Repository,為什麼Repository會有這麼大的爭議,博主認為主要原因無非以下兩點:一是Repository的真實意圖沒有理解清楚,導致設計的紊亂,隨著專案的橫向和縱向擴充套件,

C#系列——DDD領域驅動設計初探Web層的搭建

前言:好久沒更新部落格了,每天被該死的業務纏身,今天正好一個模組完成了,繼續來完善我們的程式碼。之前的六篇完成了領域層、應用層、以及基礎結構層的部分程式碼,這篇打算搭建下UI層的程式碼。 DDD領域驅動設計初探系列文章: 一、UI層介紹 在DDD裡面,UI層的設計也分為BS和CS,本篇還是以Web為

C#系列——WebApi 異常處理解決方案

機制 輸出 ges 如果 但是 rom lba slist 解決 出處:http://www.cnblogs.com/landeanfen/p/5363846.html 閱讀目錄 一、使用異常篩選器捕獲所有異常 二、HttpResponseException自

大型Java專題(二) 軟體架構設計原則

## 前言 ​ 今天開始我們專題的第一課了,也是我開始進階學習的第一天,我們先從經典設計思想開始,看看大牛市如何寫程式碼的,提升技術審美、提高核心競爭力。本章節參考資料書籍《Spring 5核心原理》中的第一篇 Spring 內功心法(沒有電子檔,都是我取其精華並結合自己的理解,一個字一個字手敲出來的)。

【SSH之路】Hibernate基本原理

      在開始學Hibernate之前,一直就有人說:Hibernate並不難,無非是對JDBC進一步封裝。一句不難,難道是真的不難還是眼高手低?       如果只是停留在使用的層面上,我相信什

C/C++語法淺談二十三種設計模式——工廠模式Factory Method

0.寫在前面 在軟體開發過程中,為了提高開發效率、增強軟體執行的穩定性,降低後期專案維護的成本,我們志在追求更加高效、簡單的設計思路來引領我們的專案產品,在經過不斷的探索與總結的過程中,我們最常用的設計模式有23中,總體分為三大類,即建立型模式、結構型模式和行為型模式,具體如下:

電源管理與驅動設計 筆記

1.電源管理的功能:具備電壓過高保護、電流過大保護、電量監測、過放保護等功能---->自主充電 2.一款清潔機器人的運動控制系統方案設計示意圖 2.1電源充電及其報警框圖 2.2 電源管理具體電路設計[電源的過放和過充保護:低電壓檢測+過充保護] (1)檢測電池充

量化——多策略量化回測實錄

京東金融官方資訊QQ群:456448095 有什麼想諮詢的都可以來詢問我們 移動平均線是技術分析中最常用的,作為一種簡單有效的數學模型而被廣泛使用。均線使用的方式的差異在於均線的計算方式與價格使用方式。不同的均線計算方式會產生不同的結果,不同的價格使用也會有不同的效

C# 基礎知識系列- 13 常見類庫介紹

0. 前言 每篇一個前言,介紹一下這一篇的內容。之前的內容都是針對某些知識點進行的介紹,這篇內容介紹一下實際開發中常用的一些類和名稱空間。這一篇是個連續劇,大概有個三四集。嗯,就是這樣。 1. System 名稱空間 System空間,是C#的基礎名稱空間,裡面定義了常用值和資料型別以及各種型別的基類,當然也

Java設計模式——六大設計原則

單一職責原則 里氏替換原則 依賴倒置原則 介面隔離原則 迪米特法則 開閉原則 注:準備系統學習一下Java的23種開發模式,由於Java的開發模式是根據六大設計原則設計的,所以有必要認真學一下Java的六大設計原則(圖片來源於網路,侵刪)

Java 設計模式簡單工廠模式

參考連結:簡單工廠模式-Simple Factory Pattern 1. 模式概述 定義:定義一個工廠類,它可以根據引數的不同返回不同類的例項,被建立的例項通常都具有共同的父類。因為在簡單工廠模式中用於建立例項的方法是靜態方法,因此簡單工廠模式又被稱為靜態工廠方法模式。 簡

設計模式模板模式

實際開發中常常會遇到,程式碼骨架類似甚至相同,只是具體的實現不一樣的場景。例如:流程都有開啟、編輯、駁回、結束。每個流程都包含這幾個步驟,不同的是不同的流程例項它們的內容不一樣。共享單車都是先開鎖、騎行、上鎖、付款。這些大的步驟固定,不同的是每個例項的具體實現細節不一樣。這些類似的業務我們都可以使用模板模式實

Java設計模式單例模式,防止反射和反序列化漏洞

package com.iter.devbox.singleton; import java.io.ObjectStreamException; import java.io.Serializable; /** * 靜態內部類實現方式(也是一種懶載入方式) * 這種方式:執行緒安全,呼叫效率高,並且實

影象演算法中的設計模式使用策略模式設計演算法

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。總體來說,一個設計模式就是一個可重用的、讓程式碼更容易被他人理解的、可靠性的解決方案。 策略設計模式的目的就是把演算法封裝進類。封裝後,演算法之間互相

設計模式】HeadFirst設計模式策略Strategy模式

1、概述 在軟體開發的過程中,實現一個功能可能會使用很多種演算法,一種比較常用的做法是把這些演算法或者策略寫在一個類中,一個演算法寫一個方法。當我們需要新增一個演算法的時候,我們需要修改封裝這些演算法