從壹開始微服務 [ DDD ] 之三 ║ 簡單說說:領域、子域、限界上下文

前言

哈嘍你們好,DDD領域驅動設計系列又開始了,前天週二的那篇入門文章中,也收到了必定的效果(寫小說的除外),同時我也是倍感鴨梨,怎麼說呢,DDD領域驅動設計已經有十年曆史了,甚至更久,可是包括我在內的一批技術人員仍是對其不是很明白,這幾天我也是日思夜想,怎樣才能說的明白,怎樣才能把這個高高在上的思想落在實踐上,惋惜的是國內栗子比較少,國外文章比較少,只能硬啃了,因此更須要你們一塊兒來討論,這裏要說一下,是一塊兒討論推進,而不是心裏去拒絕,而一直和多層架構作對比,這樣不只不利於學習,也沒法帶動個人積極性,因此,這裏懇請你們,多多評論,多多交流,比較我一我的很難扛得動這個DDD的大旗。html

好啦,言歸正傳,上次我們說到了《[ DDD ] 之二 ║ DDD入門 & 項目結構粗搭建》,其中主要說明了爲何使用DDD,以及如何簡單的搭建一個基於領域的粗略層,顆粒度仍是項目級別,尚未繼續往下深究,今天呢,我們就往下慢慢走,說一說整個項目下,是如何實現領域設計的。數據庫

這裏先給你們提一個問題,若是一個新的項目,好比一個小的問答系統交給你的手裏,PM 剛剛和你簡單的討論了下需求,下一步你打算作些什麼?安全

一、根據需求,馬上準備設計數據庫,建表,腦中模擬場景;架構

二、根據需求,馬上創建實體類(也就是model層),而後CodeFirst 生成數據庫;post

三、找尋該領域專家(作過或者懂得相似產品的人),設計該問答領域下,有哪些子領域,製做限界上下文;學習

四、啥都沒有,直接網上找開源項目,下載下來看看;ui

老張說:這裏沒有正確與否的比較,只是一個習慣和優劣的分析,不用太在乎,若是你比較好奇,那就往下看吧。spa

 

零、今天要完成綠色的部分

 

1、領域 —— 就是一個獨立項目

一、領域的概念

 這個概念相信不少人已經很明白了,並且也聽到了無數遍,這裏就再簡單的說兩句:設計

領域(Domain)其實就是一個組織所要作的整個事情,已經這個事情下所包含的一切內容。這是一個範圍概念,並且是面向業務的(注意這裏不是面向技術的,更不是面向數據庫的持久化的),每一個組織都有本身的人員、本身的工做業務範圍和作事方式,當你爲該組織開發軟件的時候,你面對的就是這個組織的領域。日誌

就好比以前我在一家旅遊公司進行開發工做,那我所進行的開發工做就是一個旅遊行業,我必需要很清晰旅遊行業的其中的領域知識,並且必須能和領域專家經過通用性語言進行溝通,這樣能保證我開發出來的是他們想要的,而不是我單純的從技術上實現,在領域設計上一塌糊塗。固然咱們天天也都在作這樣的事情,也許你感受很正常,那我再舉個例子:

我在開發其中一個目的地(旅遊景點)項目的時候,這是一個領域,後來在電商系統項目中,又是一個領域,可是在電商領域中,涉及到了景點領域的一些數據,那我若是不和領域專家溝通,有時候爲了貪圖技術上的方便,甚至把兩個領域合併成一個,雖然都不大,合併之後大小也還能夠,可是這樣卻徹底打破了領域的這個概念,這個就是徹底面向技術開發的,由於領域專家看不懂我這麼寫到底屬於什麼。

固然上邊的栗子有點兒牽強,我們再說下之後我想作的一個基於DDD的問答項目,我們先畫一個框。

就如圖所示,我們首先定義一個邊界,至於裏邊有什麼東西,我們接着往下看,這個很簡單。

二、如何定義一個領域

這個是更簡單的一個問題,在領域設計中,有兩個方法:戰略設計和戰術設計,其實我我的感受能夠定義兩步走,這兩個是有前後之分的,

戰略設計中定義了,一個領域就是一個問題空間,咱們在業務中所遇到的全部的問題與挑戰;

在戰術設計中,一個領域就算一個解決問題空間,用來解決在問題空間的全部問題;

因此,其實一個領域就是一個咱們創建的一個解決方案,一個項目,在咱們的問答項目中,整個解決方案就是一個問答領域。

 

2、子領域 —— 具體的項目實現

一、子域 / 核心子領域 / 通用子領域

什麼是子域(SubDomain)呢?這個很好理解,就是在整個領域中,咱們如何對其進行拆分,而後知足咱們的業務邏輯。一個子域多是一個 dll ,一個命名空間的形式存在。

咱們定義好領域,而且劃分好限界後,就開始考慮如何進行實現,這裏你們想想如何設計與劃分,這裏就說說我本身的以前的想法:

在咱們的問答領域設計中,咱們的思路必定是有客戶來 =》驗證是否有發問題的權限 =》 而後發佈一個問題 =》

這僅僅是一個發佈問題的流程,也僅僅是一個顧客認證的過程,很簡單,咱們通常會怎麼分子領域呢,可能會這麼分,這個就是 消息發佈子領域,裏邊有咱們的發佈模型,用戶模型,討論模型,日曆模型等等,大概就是這個樣子

由於咱們會這麼想:「用戶和權限這兩個模型,和咱們的消息子領域有何緊密的關係,你看,發佈+回覆+討論+日曆(指本身新建一個日曆功能,具體待定),這些模型確定都須要用戶登錄認證吧,甚至有些是須要受權的,分在一個子領域有什麼不對麼?」,這樣的代碼邏輯應該是這樣的

 

若是是你看到這裏,首先明白了什麼是子領域了吧,也知道如何劃分了,可是你感受這個劃分對麼? 若是你感受很正常,那就請往下看吧。

 

二、核心子領域 / 通用子領域 / 支撐子領域

咱們再來分析一下,咱們的問答領域中的有哪些內容,首先:確定有消息發佈子領域,這個也是上邊說到的,這個毋庸置疑,一個問答系統,消息發佈是確定的(這裏說明下:發佈問題,回答問題,討論問題等都屬於一個消息的發佈,這個應該理解),並且這個子領域是缺乏它不可的,這個就是咱們的核心子領域

再來看看,還有一些其餘的,好比日誌記錄,數據操做痕跡記錄(哪一個管理員修改了哪些數據),這些子領域貫穿着咱們整個領域系統,被其餘領域共用,咱們稱之爲 通用子領域

固然,咱們還有一些站內的即時消息,wiki百科,通知提醒,活動跟蹤,等等,這些都不是咱們的核心子域,由於沒有這些,咱們依然能夠進行問答,可是這些確是支撐着咱們核心子域的相關功能,咱們就把這些命名爲 支撐子領域,這個時候你會問,這些支持子領域要不要再拆開,我我的表示沒有很大的必要。

最後咱們再來看看咱們上邊的用戶認證受權問題,在上邊咱們把他們柔和到了消息核心子域裏,可是這裏要說明,這二者是沒有關係的:

爲何沒有關係呢?誠然,咱們的軟件是必須有用戶參與的,可是咱們應該將不一樣的用戶種類區別對待,由於在不一樣的上下文(下邊會說到)中,他們的做用和任務是不同的,在消息核心子域中,咱們關注的是角色,無論他是誰或者有什麼權限,若是咱們有一天把權限模型修改了,那咱們的問答模型也必定要修改,你想一想是否是,由於二者業務邏輯已經耦合了!

這個時候咱們應該明白,發佈信息和「誰能夠發,在什麼條件下發」其實沒有太大的關係,個人問答,只關心的是「有一個顧客發佈了一個問題」這樣就能夠了,咱們關心的是發佈消息這個過程,而不能把用戶權限涉及進來,這個時候咱們應該把用戶權限單拿出來一個子領域,就叫安全子領域。

 

三、隔離內核

其實上邊說的可能有點兒朦朧,可是咱們應該都已經用到了,若是你看了個人上一個系列教程,你應該知道有一個JWT權限驗證那一章節,不少人就是不很理解,是如何進行受權驗證的,其實採用的就是隔離內容,之前咱們寫邏輯,就算直接在控制器裏,判斷當前用戶權限,可是如今咱們是經過一箇中間件,判斷 Token 所包含的用戶Role 是否有這個權限,再進行下一步,只不過在DDD中,把這一塊單拿出來造成了一個安全子領域了,這個時候你應該明白了吧。

        /// <summary>
        /// 刪除一個顧客信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost, ActionName("Delete")]
        [Authorize(Policy = "CanRemoveCustomerData")]
        [Route("customer-management/remove-customer/{id:guid}")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(Guid id)
        {
            _customerAppService.Remove(id);

            if (!IsValidOperation()) return View(_customerAppService.GetById(id));

            ViewBag.Sucesso = "Customer Removed!";
            return RedirectToAction("Index");
        }

 

這個時候,可能還不是很明白,爲何好好的程序要拆分,這麼作的目的又是爲了什麼,直接在須要用到的權限的地方寫業務邏輯不就好了麼,這個往下看,我們說說限界上下文。

 

3、限界上下文 —— 領域模型的邊界

一、限界上下文是顯示的,有語義的

限界上下文(Bounded Context)定義了每一個模型的應用範圍,在每一個Bounded Context中確保領域模型的一致性。不一樣的限界上下文中,領域模型能夠不用保證一致性。一般咱們根據團隊的組織、軟件系統的每一個部分的用法及物理表現(如組件劃分,數據庫模式)來設置模型的邊界。

概念仍是有點兒朦朧,那就舉例來講:

在電商系統中,銷售子域是核心域,商品子域和物流子域爲支撐子域。在這三個子域中,都要和商品打交道。若是把商品抽象爲Product對象的話,按咱們通常的常規思路(拋開子域的劃分)來講,無論是商品銷售仍是發貨,咱們均可以共用同一個Product對象。
但在DDD中,在商品子域和銷售子域中,能夠共享這個Product對象,但在物流子域,就有點大材小用。爲何呢?由於畢竟物流子域關注的是商品的發貨處理和物流跟蹤。針對發貨流程而言,我只關心商品的數量、大小、重量等規格,而沒必要了解商品的價格等其餘信息。因此說物流子域應該關注的是貨物的發貨處理而不是商品。
那爲何咱們以前的開發思路會共用同一個Product對象呢?
答案很簡單,沒有進行領域的劃分。把整個項目一律而論,統一建模致使的結果。
在DDD的思想下,當劃分子域以後,每一個子域都對應有各自的上下文。在銷售子域和商品子域所在的上下文語境中,商品就是商品,無二義性。在物流子域的上下文語境中,咱們也能夠說商品的發貨處理,但這時的商品就特指貨物了。肯定了真實面目以後,我想咱們也會情不自禁的抽象一個新的Cargo對象來處理物流相關的業務。這也是DDD帶來的好處,讓咱們更清晰的建模。

 

二、定義限界上下文

 在咱們上邊的子域定義中,咱們出現了三個子域,我這裏同時也定義了三個限界上下文(這裏說下,二者不是一對一的關係),整體來講,咱們不該該按技術架構或者開發任務來建立限界上下文,應該按照語義的邊界來考慮。

咱們的實踐是,考慮產品所講的通用語言,從中提取一些術語稱之爲概念對象,尋找對象之間的聯繫;或者從需求裏提取一些動詞,觀察動詞和對象之間的關係;咱們將緊耦合的各自圈在一塊兒,觀察他們內在的聯繫,從而造成對應的界限上下文。造成以後,咱們能夠嘗試用語言來描述下界限上下文的職責,看它是否清晰、準確、簡潔和完整。簡言之,限界上下文應該從需求出發,按領域劃分。

 

 

三、上下文都包含哪些內容

一個限界上下文不是隻有領域模型,固然這個是必不可少的,它整體來講是一個系統,一個應用程序,或者一個業務服務,它裏邊會有實體,值對象,領域事件(一個個方法事件組成,好比用戶註冊,修改密碼,驗證信息等等都是該上下文中的領域事件),在咱們的身份和訪問上下文中,是這樣定義的

 

感受寫到這裏仍是沒有寫的很透徹,由於咱們尚未涉及到代碼,可能經過代碼的設計會比較好。

 

4、結語

 本文主要是經過DDD領域設計的思想,來講明如何對一個項目進行細分的過程,這個再想一想文章開頭提出的問題,是否是稍微有些感觸,只不過在沒有代碼的講解下,一塊兒老是很空洞,下次我們直接經過基礎設施層中的上下文定義,來進一步瞭解領域設計的思想吧。 

相關文章
相關標籤/搜索