從壹開(kāi)始微服務(wù) [ DDD ] 之十 ║領(lǐng)域驅(qū)動(dòng)【實(shí)戰(zhàn)篇·中】:命令總線Bus分發(fā)(一)

烽火

哈嘍大家好,老張又見(jiàn)面了桩撮,這兩天被各個(gè)平臺(tái)的“雞湯貼”差點(diǎn)亂了心神敦第,博客園如此,簡(jiǎn)書(shū)亦如此距境,還好群里小伙伴及時(shí)提醒申尼,路還很長(zhǎng)垮卓,這些小事兒就隨風(fēng)而去吧垫桂,這周本不打算更了,但是被群里小伙伴“催稿”了粟按,至少也是對(duì)我的一個(gè)肯定吧诬滩,又開(kāi)始熬夜中,請(qǐng)@初久小伙伴留言灭将,我不知道你的地址疼鸟,就不放鏈接了。

收住庙曙,言歸正傳空镜,上次咱們說(shuō)到了領(lǐng)域命令驗(yàn)證《九 ║從軍事故事中,明白領(lǐng)域命令驗(yàn)證(上)》捌朴,也介紹了其中的兩個(gè)角色——領(lǐng)域命令模型和命令驗(yàn)證吴攒,這些都是屬于領(lǐng)域?qū)拥母拍睿?dāng)然這里的內(nèi)容是 命令 砂蔽,查詢就當(dāng)然不需要這個(gè)了洼怔,查詢的話,直接從倉(cāng)儲(chǔ)中獲取值就行了左驾,很簡(jiǎn)單镣隶。也沒(méi)人問(wèn)我問(wèn)題极谊,那我就權(quán)當(dāng)大家已經(jīng)對(duì)上篇都看懂了,這里就不再贅述安岂。不知道大家是否還記得上篇文章末尾轻猖,提到的幾個(gè)問(wèn)題,我這里再提一下嗜闻,就是今天的提綱了蜕依,如果你今天看完本篇,這幾個(gè)問(wèn)題能回答上來(lái)琉雳,那恭喜样眠,你就明白了今天所講的問(wèn)題:

1、命令模型RegisterStudentCommand 放到 Controller 中真的好么翠肘?//我們平時(shí)都是這么做的

2檐束、如果不放到Controller里調(diào)用,我們?nèi)绻{(diào)用束倍?在 Service里么被丧?//也是一個(gè)辦法,至少Controller干凈了绪妹,但是 Service 就重了

3甥桂、驗(yàn)證的結(jié)果又如何獲取并在前臺(tái)展示呢?//本文會(huì)先用一個(gè)錯(cuò)誤的方法來(lái)說(shuō)明問(wèn)題邮旷,下篇會(huì)用正確的

4黄选、如何把領(lǐng)域模型 Student 從應(yīng)用層 StudentAppService 解耦出去( Register()方法中 )。//本文重點(diǎn)婶肩,中介者模式

好啦办陷,簡(jiǎn)單先寫(xiě)這四個(gè)問(wèn)題吧,這個(gè)時(shí)候你可以先不要從 Github 上拉取代碼律歼,先看著目前手中的代碼民镜,然后思考這四個(gè)問(wèn)題,如果要是自己险毁,或者咱們以前是怎么做的制圈,如果你看過(guò)以后會(huì)有一些新的認(rèn)識(shí)和領(lǐng)悟,請(qǐng)幫忙評(píng)論一下畔况,捧個(gè)人場(chǎng)嘛鲸鹦,是吧??。好啦问窃,今天的東西可能有點(diǎn)兒多亥鬓,請(qǐng)做好大概半個(gè)小時(shí)的準(zhǔn)備,當(dāng)然這半個(gè)小時(shí)你需要思考域庇,要是走馬觀花嵌戈,肯定是收獲沒(méi)有那么多的覆积,代碼已經(jīng)更新了,記得看完的時(shí)候 pull 一下代碼熟呛。

讀前必讀

1宽档、本文中可能會(huì)涉及比較多的依賴注入,請(qǐng)一定要看清楚庵朝,因?yàn)檫@是第二個(gè)系列了吗冤,有時(shí)候小細(xì)節(jié)就不點(diǎn)明了,需要大家有一定的基礎(chǔ)九府,可以看我第一個(gè)系列椎瘟。

2、這三篇核心內(nèi)容侄旬,都是重點(diǎn)在領(lǐng)域?qū)臃挝担?qǐng)一定要多思考。

3儡羔、文章不僅有代碼宣羊,更多的是理解,比如用聯(lián)合國(guó)的栗子來(lái)說(shuō)明中介者模式汰蜘,請(qǐng)務(wù)必要多思考仇冯。

零、今天實(shí)現(xiàn)左下角淺紫色的部分

image

一族操、什么是中介者模式苛坚?

1、中介模式的概念

這個(gè)其實(shí)很好理解坪创,單單從名字上大家也都能理解它是一個(gè)什么模式炕婶,因?yàn)楸疚牡闹攸c(diǎn)不是一個(gè)講解什么是23種設(shè)計(jì)模式的姐赡,大家有興趣的可以好好的買本書(shū)莱预,或者找找資料,好好项滑,主要是思想依沮,不需要自己寫(xiě)一個(gè)項(xiàng)目,如果大家有需要枪狂,可以留言危喉,我以后單寫(xiě)一篇文章,介紹中介者模式州疾。

這里就摘抄一段定義吧:

中介者模式是一個(gè)行為設(shè)計(jì)模式辜限,它允許我們公開(kāi)一個(gè)統(tǒng)一的接口,系統(tǒng)的 **不同部分 **可以通過(guò)該接口進(jìn)行 通信严蓖,而 **不需要 **顯示的相互作用薄嫡;

適用場(chǎng)景:如果一個(gè)系統(tǒng)的各個(gè)組件之間看起來(lái)有太多的直接關(guān)系(就比如我們系統(tǒng)中那么多模型對(duì)象氧急,下邊會(huì)解釋),這個(gè)時(shí)候則需要一個(gè)中心控制點(diǎn)毫深,以便各個(gè)組件可以通過(guò)這個(gè)中心控制點(diǎn)進(jìn)行通信吩坝;

該模式促進(jìn)松散耦合的方式是:確保組件的交互是通過(guò)這個(gè)中心點(diǎn)來(lái)進(jìn)行處理的,而不是通過(guò)顯示的引用彼此哑蔫;

比如系統(tǒng)和各個(gè)硬件钉寝,系統(tǒng)作為中介者,各個(gè)硬件作為同事者闸迷,當(dāng)一個(gè)同事的狀態(tài)發(fā)生改變的時(shí)候嵌纲,不需要告訴其他每個(gè)硬件自己發(fā)生了變化,只需要告訴中介者系統(tǒng)腥沽,系統(tǒng)會(huì)通知每個(gè)硬件某個(gè)硬件發(fā)生了改變疹瘦,其他的硬件會(huì)做出相應(yīng)的變化;

這樣巡球,之前是網(wǎng)狀結(jié)構(gòu)言沐,現(xiàn)在變成了以中介者為中心的星星結(jié)構(gòu):

image

是不是挺像一個(gè)容器的,他自己把控著整個(gè)流程酣栈,和每一個(gè)對(duì)象都有或多或少险胰,或近或遠(yuǎn)的聯(lián)系,多個(gè)對(duì)象之間不用理睬其他對(duì)象發(fā)生了什么矿筝,只是負(fù)責(zé)自己的模塊就好起便,然后把消息發(fā)給中介者,讓中介者再分發(fā)給其他的具體對(duì)象窖维,從而實(shí)現(xiàn)通訊 —— 這個(gè)思想就是中介者的核心思想榆综,而且也是DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的核心思想之一( 還有一個(gè)核心思想是領(lǐng)域設(shè)計(jì)的思想 ),這里你可能還是不那么直觀铸史,我剛剛花了一個(gè)小時(shí)鼻疮,對(duì)咱們的DDD框架中的中介者模式畫(huà)了一個(gè)圖,相信會(huì)有一些新的認(rèn)識(shí)琳轿,在下邊第 3 點(diǎn)會(huì)看到判沟,請(qǐng)耐心往下看。

2崭篡、中介模式的原理

這里有一個(gè)聯(lián)合國(guó)的栗子挪哄,也是常用來(lái)介紹和解釋中介者模式的栗子:

抽象中介者(AbstractMediator):定義中介者和各個(gè)同事者之間的通信的接口;//比如下文提到的 抽象聯(lián)合國(guó)機(jī)構(gòu)

抽象同事者(AbstractColleague):定義同事者和中介者通信的接口琉闪,實(shí)現(xiàn)同事的公共功能迹炼;//比如下文中的 抽象國(guó)家

中介者(ConcreteMediator):需要了解并且維護(hù)每個(gè)同事對(duì)象,實(shí)現(xiàn)抽象方法颠毙,負(fù)責(zé)協(xié)調(diào)和各個(gè)具體的同事的交互關(guān)系斯入;//比如下文中的 聯(lián)合國(guó)安理會(huì)

同事者(ConcreteColleague):實(shí)現(xiàn)自己的業(yè)務(wù)拿霉,并且實(shí)現(xiàn)抽象方法,和中介者進(jìn)行通信咱扣;//比如下文的 美國(guó)绽淘、英國(guó)、伊拉克等國(guó)家

注意:其中同事者是多個(gè)同事相互影響的才能叫做同事者闹伪;

還是希望大家能好好看看沪铭,好好想想,如果你還沒(méi)有接觸過(guò)這個(gè)中介者模式偏瓤,如果了解并使用過(guò)杀怠,就簡(jiǎn)單看一看,要是你能把這個(gè)小栗子看懂了厅克,那下邊的內(nèi)容赔退,就很容易了,甚至是以后的內(nèi)容就如魚(yú)得水了证舟,畢竟DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)兩個(gè)核心就是:CQRS讀寫(xiě)分離 + 中介者模式 硕旗。

image

這個(gè)下邊是一個(gè)簡(jiǎn)單的Demo,可以簡(jiǎn)單的看一看:

namespace 中介者模式
{
    class Program
    {
        static void Main(string[] args)
        {
            //實(shí)例化 具體中介者 聯(lián)合國(guó)安理會(huì)
            UnitedNationsSecurityCouncil UNSC = new UnitedNationsSecurityCouncil();

            //實(shí)例化一個(gè)美國(guó)
            USA c1 = new USA(UNSC);
            //實(shí)例化一個(gè)里拉開(kāi)
            Iraq c2 = new Iraq(UNSC);

            //將兩個(gè)對(duì)象賦值給安理會(huì)
            //具體的中介者必須知道全部的對(duì)象
            UNSC.Colleague1 = c1;
            UNSC.Colleague2 = c2;

            //美國(guó)發(fā)表聲明女责,伊拉克接收到
            c1.Declare("不準(zhǔn)研制核武器漆枚,否則要發(fā)動(dòng)戰(zhàn)爭(zhēng)!");
            //伊拉克發(fā)表聲明抵知,美國(guó)收到信息
            c2.Declare("我們沒(méi)有核武器墙基,也不怕侵略。");

            Console.Read();
        }
    }
    /// <summary>
    /// 聯(lián)合國(guó)機(jī)構(gòu)抽象類
    /// 抽象中介者
    /// </summary>
    abstract class UnitedNations
    {
        /// <summary>
        /// 聲明
        /// </summary>
        /// <param name="message">聲明信息</param>
        /// <param name="colleague">聲明國(guó)家</param>
        public abstract void Declare(string message, Country colleague);
    }
    /// <summary>
    /// 聯(lián)合國(guó)安全理事會(huì),它繼承 聯(lián)合國(guó)機(jī)構(gòu)抽象類
    /// 具體中介者
    /// </summary>
    class UnitedNationsSecurityCouncil : UnitedNations
    {
        //美國(guó) 具體國(guó)家類1
        private USA colleague1;
        //伊拉克 具體國(guó)家類2
        private Iraq colleague2;

        public USA Colleague1
        {
            set { colleague1 = value; }
        }
        public Iraq Colleague2
        {
            set { colleague2 = value; }
        }
        //重寫(xiě)聲明函數(shù)
        public override void Declare(string message, Country colleague)
        {
            //如果美國(guó)發(fā)布的聲明刷喜,則伊拉克獲取消息
            if (colleague == colleague1)
            {
                colleague2.GetMessage(message);
            }
            else//反之亦然
            {
                colleague1.GetMessage(message);
            }
        }
    }
    /// <summary>
    /// 國(guó)家抽象類
    /// </summary>
    abstract class Country
    {
        //聯(lián)合國(guó)機(jī)構(gòu)抽象類
        protected UnitedNations mediator;

        public Country(UnitedNations mediator)
        {
            this.mediator = mediator;
        }
    }
    /// <summary>
    /// 美國(guó) 具體國(guó)家類
    /// </summary>
    class USA : Country
    {
        public USA(UnitedNations mediator)
            : base(mediator)
        {
        }
        //聲明方法残制,將聲明內(nèi)容較給抽象中介者 聯(lián)合國(guó)
        public void Declare(string message)
        {
            //通過(guò)抽象中介者發(fā)表聲明
            //參數(shù):信息+類
            mediator.Declare(message, this);
        }
        //獲得消息
        public void GetMessage(string message)
        {
            Console.WriteLine("美國(guó)獲得對(duì)方信息:" + message);
        }
    }
    /// <summary>
    /// 伊拉克 具體國(guó)家類
    /// </summary>
    class Iraq : Country
    {
        public Iraq(UnitedNations mediator)
            : base(mediator)
        {
        }
        //聲明方法,將聲明內(nèi)容較給抽象中介者 聯(lián)合國(guó)
        public void Declare(string message)
        {
            //通過(guò)抽象中介者發(fā)表聲明
            //參數(shù):信息+類
            mediator.Declare(message, this);
        }
        //獲得消息
        public void GetMessage(string message)
        {
            Console.WriteLine("伊拉克獲得對(duì)方信息:" + message);
        }
    }
}

最終的結(jié)果是:

image

從這個(gè)小栗子中掖疮,也許你能看出來(lái)初茶,美國(guó)和伊拉克之間,對(duì)象之間并沒(méi)有任何的交集和聯(lián)系氮墨,但是他們之間卻發(fā)生了通訊纺蛆,各自獨(dú)立吐葵,但是又相互通訊规揪,這個(gè)不就是很好的實(shí)現(xiàn)了解耦的作用么!一切都是通過(guò)中介者來(lái)控制温峭,當(dāng)然這只是一個(gè)小栗子猛铅,咱們推而廣之:

命令模式、消息通知模型凤藏、領(lǐng)域模型等奸忽,內(nèi)部運(yùn)行完成后堕伪,將產(chǎn)生的信息拋向給中介者,然后中介者再根據(jù)情況分發(fā)給各個(gè)成員(如果又需要的)栗菜,這樣就實(shí)現(xiàn)多個(gè)對(duì)象的解耦欠雌,而且也達(dá)到同步的作用,當(dāng)然還有一些輔助知識(shí):異步疙筹、注入富俄、事件等,咱們慢慢學(xué)習(xí)而咆,至少現(xiàn)在中介者模式的思想和原理你應(yīng)該都懂了霍比。

3、本項(xiàng)目是如何使用中介者模式的

相信如果你是從我的第一篇文章看下去的暴备,一定會(huì)以下幾個(gè)模型很熟悉:視圖模型悠瞬、領(lǐng)域模型、命令模型涯捻、驗(yàn)證(上次說(shuō)的)浅妆、還有沒(méi)有說(shuō)到的通知模型,如果你對(duì)這幾個(gè)名稱還很朦朧障癌,請(qǐng)現(xiàn)在先在腦子里仔細(xì)想一想狂打,不然下邊的可能會(huì)亂,如果你一看到名字就能理解都是干什么的混弥,都是什么作用趴乡,那好,請(qǐng)看下邊的關(guān)系圖蝗拿。

首先咱們看看晾捏,如果不適用中介者模式,會(huì)是什么狀態(tài):

image

這個(gè)時(shí)候你會(huì)說(shuō)哀托,不惦辛!我不信會(huì)這么復(fù)雜!是真的么仓手?我們的視圖模型肯定和命令模型有交互吧胖齐,命令模型和領(lǐng)域模型肯定也有吧,那命令中有錯(cuò)誤信息吧嗽冒,肯定要交給通知模型的呀伙,說(shuō)到這里,你應(yīng)該會(huì)感覺(jué)可能真的有一些復(fù)雜的交互添坊,當(dāng)然剿另!也可能沒(méi)有那么復(fù)雜,我們平時(shí)就是一個(gè)實(shí)體 model 走天下的,錯(cuò)誤信息隨便返回給字符串呀雨女,等等諸如此類谚攒。

如果你承認(rèn)了這個(gè)結(jié)構(gòu)很復(fù)雜,那好氛堕!咱們看看中介者模式會(huì)是什么樣子的馏臭,可能你看著會(huì)更復(fù)雜,但是會(huì)很清晰:

image

(這可是老張花了一個(gè)小時(shí)畫(huà)的讼稚,兄弟給個(gè)贊??吧)

不知道你看到這里會(huì)不會(huì)腦子一嗡位喂,沒(méi)關(guān)系,等這個(gè)系列說(shuō)完了乱灵,你就會(huì)明白了塑崖,今天咱們就主要說(shuō)的是其中一個(gè)部分,**命令總線 Command Bus痛倚、命令處理程序规婆、工作單元的提交 **這三塊:

image

從上邊的大圖中,我們看到蝉稳,本來(lái)交織在一起的多個(gè)模型抒蚜,本一條虛擬的流程串了起來(lái),這里邊就包括CQRS讀寫(xiě)分離思想 和 中介者模型耘戚,當(dāng)然還有人說(shuō)是發(fā)布-訂閱模型嗡髓,這個(gè)我還在醞釀,以后的文章會(huì)說(shuō)到收津。雖然對(duì)象還是那么多饿这,但是清晰了起來(lái),多個(gè)對(duì)象之間也沒(méi)有存在一個(gè)很深的聯(lián)系撞秋,讓業(yè)務(wù)之間更加專注自身業(yè)務(wù)长捧。

如果你現(xiàn)在對(duì)中介者模式已經(jīng)有了一定的意識(shí),也知道了它的作用和意思吻贿,那它到底是如何操作的呢串结,請(qǐng)耐心往外看,重點(diǎn)來(lái)了舅列。

二肌割、創(chuàng)建命令總線 Command Bus

1、創(chuàng)建一個(gè)中介處理程序接口

在我們的核心領(lǐng)域?qū)?Christ3D.Domain.Core 中帐要,新建 Bus 文件夾把敞,然后創(chuàng)建中介處理程序接口 IMediatorHandler.cs

namespace Christ3D.Domain.Core.Bus
{ 
    /// <summary>
    /// 中介處理程序接口 
    /// 可以定義多個(gè)處理程序 
    /// 是異步的 
    /// </summary>
    public interface IMediatorHandler
    { 
        /// <summary>
        /// 發(fā)布命令,將我們的命令模型發(fā)布到中介者模塊 
        /// </summary>
        /// <typeparam name="T"> 泛型 </typeparam>
        /// <param name="command"> 命令模型宠叼,比如RegisterStudentCommand </param>
        /// <returns></returns>
        Task SendCommand<T>(T command) where T : Command;
    }
}

發(fā)布命令:就好像我們調(diào)用某招聘平臺(tái)先巴,發(fā)布了一個(gè)招聘命令其爵。

2冒冬、一個(gè)低調(diào)的中介者工具 —— MediatR

微軟官方eshopOnContainer開(kāi)源項(xiàng)目中使用到了該工具伸蚯,

mediatR 是一種中介工具,解耦了消息處理器和消息之間耦合的類庫(kù)简烤,支持跨平臺(tái) .net Standard和.net framework
https://github.com/jbogard/MediatR/wiki 這里是原文地址剂邮。其作者也是Automapper的作者。
功能要是簡(jiǎn)述的話就倆方面:
request/response 請(qǐng)求響應(yīng) //咱們就采用這個(gè)方式
pub/sub 發(fā)布訂閱

使用方法:通過(guò) .NET CORE 自帶的 IoC 注入

引用 MediatR nuget:install-package MediatR

引用IOC擴(kuò)展 nuget:installpackage MediatR.Extensions.Microsoft.DependencyInjection //擴(kuò)展包

使用方式:

services.AddMediatR(typeof(MyxxxHandler));//單單注入某一個(gè)處理程序

services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);//目的是為了掃描Handler的實(shí)現(xiàn)對(duì)象并添加到IOC的容器中

//參考示例

//請(qǐng)求響應(yīng)方式(request/response)横侦,三步走:
//步驟一:創(chuàng)建一個(gè)消息對(duì)象挥萌,需要實(shí)現(xiàn)IRequest,或IRequest<> 接口,表明該對(duì)象是處理器的一個(gè)對(duì)象
public class Ping : IRequest<string>
{
}

//步驟二:創(chuàng)建一個(gè)處理器對(duì)象
public class PingHandler : IRequestHandler<Ping, string>
{
    public Task<string> Handle(Ping request, CancellationToken cancellationToken)
    {
        return Task.FromResult("老張的哲學(xué)");
    }
}

//步驟三:最后枉侧,通過(guò)mediator發(fā)送一個(gè)消息
var response = await mediator.Send(new Ping());

Debug.WriteLine(response); // "老張的哲學(xué)"

3引瀑、項(xiàng)目中實(shí)現(xiàn)中介處理程序接口

這里就不講解為什么要使用 MediatR 來(lái)實(shí)現(xiàn)我們的中介者模式了,因?yàn)槲覜](méi)有找到其他的??榨馁,具體的使用方法很簡(jiǎn)單憨栽,就和我們的緩存 IMemoryCache 一樣,通過(guò)注入翼虫,調(diào)用該接口即可屑柔,如果你還是不清楚的話,先往下看吧珍剑,應(yīng)該也能看懂掸宛。

添加 nuget 包:MediatR

注意:我這里把包安裝到了Christ3D.Domain.Core 核心領(lǐng)域?qū)恿耍驗(yàn)檫€記得上邊的那個(gè)大圖么招拙,我說(shuō)到的唧瘾,一條貫穿項(xiàng)目的線,所以這個(gè)中介處理程序接口在其他地方也用的到(比如領(lǐng)域?qū)樱┍鸱铮晕以诤诵念I(lǐng)域?qū)优蓿惭b了這個(gè)nuget包。注意安裝包后闻妓,需要編譯下當(dāng)前項(xiàng)目菌羽。

image

新建一個(gè)類庫(kù) Christ3D.Infra.Bus

當(dāng)然你也可以把它和接口 IMediatorHandler 放在一起,不過(guò)我個(gè)人感覺(jué)不是很舒服由缆,因?yàn)檫@個(gè)具體的實(shí)現(xiàn)過(guò)程注祖,不是我們領(lǐng)域設(shè)計(jì)需要知道的,就好像我們的 EFCore 倉(cāng)儲(chǔ)均唉,我們就是在領(lǐng)域?qū)邮浅浚⒘藗}(cāng)儲(chǔ)接口,然后再在基礎(chǔ)設(shè)施數(shù)據(jù)層 Christ3D.Infrastruct.Data 中實(shí)現(xiàn)的舔箭,所以為了保持一致性罩缴,我就新建了這個(gè)類庫(kù)項(xiàng)目蚊逢,用來(lái)實(shí)現(xiàn)我們的中介處理程序接口。

注意下箫章,Bus總線類庫(kù)是需要引用 Domain.Core 核心領(lǐng)域?qū)拥睦雍桑晕覀円院笤?Domain領(lǐng)域?qū)樱苯右?Bus總線層即可檬寂。

image

實(shí)現(xiàn)我們的中介處理程序接口

namespace Christ3D.Infra.Bus
{ /// <summary>
    /// 一個(gè)密封類终抽,實(shí)現(xiàn)我們的中介記憶總線 /// </summary>
    public sealed class InMemoryBus : IMediatorHandler
    { //構(gòu)造函數(shù)注入
        private readonly IMediator _mediator; public InMemoryBus(IMediator mediator)
        {
            _mediator = mediator;
        } /// <summary>
        /// 實(shí)現(xiàn)我們?cè)贗MediatorHandler中定義的接口 /// 沒(méi)有返回值 /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="command"></param>
        /// <returns></returns>
        public Task SendCommand<T>(T command) where T : Command
        { return _mediator.Send(command);//這里要注意下 command 對(duì)象
        }

    }
}

這個(gè)send方法,就是我們的中介者來(lái)替代對(duì)象桶至,進(jìn)行命令的分發(fā)昼伴,這個(gè)時(shí)候你可以會(huì)發(fā)現(xiàn)報(bào)錯(cuò)了,我們F12看看這個(gè)方法:

image

可以看到 send 方法的入?yún)⒘鸵伲仨毷荕ediarR指定的 IRequest 對(duì)象圃郊,所以,我們需要給我們的 Command命令基類女蜈,再繼承一個(gè)抽象類:


image

這個(gè)時(shí)候持舆,我們的中介總線就搞定了。

4鞭光、刪除命令模型在Controller中的使用

1吏廉、把領(lǐng)域命令模型 從 controller 中去掉

只需要一個(gè)service調(diào)用即可

image

這個(gè)時(shí)候我們文字開(kāi)頭的第一個(gè)問(wèn)題就出現(xiàn)了,我們先把 Controller 中的命令模型驗(yàn)證去掉惰许,然后在我們的應(yīng)用層 Service 中調(diào)用席覆,這里先看看文章開(kāi)頭的第二個(gè)問(wèn)題方法(當(dāng)然是不對(duì)的方法):

        public void Register(StudentViewModel StudentViewModel)
        {          
            RegisterStudentCommand registerStudentCommand = new RegisterStudentCommand(studentViewMod.........ewModel.Phone); //如果命令無(wú)效,證明有錯(cuò)誤
            if (!registerStudentCommand.IsValid())
            {
                List<string> errorInfo = new List<string>(); //獲取到錯(cuò)誤汹买,請(qǐng)思考這個(gè)Result從哪里來(lái)的 //..... //對(duì)錯(cuò)誤進(jìn)行記錄佩伤,還需要拋給前臺(tái)
                ViewBag.ErrorData = errorInfo;
            }
            _StudentRepository.Add(_mapper.Map<Student>(StudentViewModel));
            _StudentRepository.SaveChanges();

        }

且不說(shuō)這里邊語(yǔ)法各種有問(wèn)題(比如不能用 ViewBag ,當(dāng)然你可能會(huì)說(shuō)用緩存)晦毙,單單從整體設(shè)計(jì)上就很不舒服生巡,這樣僅僅是從api接口層,挪到了應(yīng)用服務(wù)層见妒,這一塊明明是業(yè)務(wù)邏輯孤荣,業(yè)務(wù)邏輯就是領(lǐng)域問(wèn)題,應(yīng)該放到領(lǐng)域?qū)印?/p>

而且還有文章說(shuō)到的第四個(gè)問(wèn)題须揣,這里也沒(méi)有解決盐股,就是這里依然有領(lǐng)域模型 Student ,沒(méi)有實(shí)現(xiàn)命令模型耻卡、領(lǐng)域模型等的交互通訊疯汁。

說(shuō)到這里,你可能腦子里有了一個(gè)大膽的想法卵酪,還記得上邊說(shuō)的中介者模式么幌蚊,就是很好的實(shí)現(xiàn)了多個(gè)對(duì)象之間的通訊谤碳,還不破壞各自的內(nèi)部邏輯,使他們只關(guān)心自己的業(yè)務(wù)邏輯溢豆,那具體如果使用呢蜒简,請(qǐng)往下看。

5沫换、在 StudentAppService 服務(wù)中臭蚁,調(diào)用中介處理接口

通過(guò)構(gòu)造函數(shù)注入我們的中介處理接口最铁,這個(gè)大家應(yīng)該都會(huì)了吧

 //注意這里是要IoC依賴注入的讯赏,還沒(méi)有實(shí)現(xiàn)
 private readonly IStudentRepository _StudentRepository; //用來(lái)進(jìn)行DTO
 private readonly IMapper _mapper; //中介者 總線
 private readonly IMediatorHandler Bus; public StudentAppService(
     IStudentRepository StudentRepository,
     IMediatorHandler bus,
     IMapper mapper
     )
 {
     _StudentRepository = StudentRepository;
     _mapper = mapper;
     Bus = bus;
 }

然后修改服務(wù)方法

 public void Register(StudentViewModel StudentViewModel)
 { //這里引入領(lǐng)域設(shè)計(jì)中的寫(xiě)命令 還沒(méi)有實(shí)現(xiàn) //請(qǐng)注意這里如果是平時(shí)的寫(xiě)法,必須要引入Student領(lǐng)域模型冷尉,會(huì)造成污染 //_StudentRepository.Add(_mapper.Map<Student>(StudentViewModel)); //_StudentRepository.SaveChanges();

     var registerCommand = _mapper.Map<RegisterStudentCommand>(StudentViewModel);
     Bus.SendCommand(registerCommand);
 }

最后記得要對(duì)服務(wù)進(jìn)行注入漱挎,這里有兩個(gè)點(diǎn)

1、ConfigureServices 中添加 MediatR 服務(wù)

// Adding MediatR for Domain Events // 領(lǐng)域命令雀哨、領(lǐng)域事件等注入 // 引用包 MediatR.Extensions.Microsoft.DependencyInjection
services.AddMediatR(typeof(Startup));

2磕谅、在我們的 Christ3D.Infra.IoC 項(xiàng)目中,注入我們的中介總線接口

 services.AddScoped<IMediatorHandler, InMemoryBus>();

老張說(shuō):這里的注入雾棺,就是指膊夹,每當(dāng)我們?cè)L問(wèn) IMediatorHandler 處理程序的時(shí)候,就是實(shí)例化 InmemoryBus 對(duì)象捌浩。

到了這里放刨,我們才完成了第一步,命令總線的定義尸饺,也就是中介處理接口的定義與使用进统,那具體是如何進(jìn)行分發(fā)的呢,我們又是如何進(jìn)行數(shù)據(jù)持久化浪听,保存數(shù)據(jù)的呢螟碎?請(qǐng)往下看,我們先說(shuō)下工作單元迹栓。

三掉分、工作單元模式 UnitOfWork

博主按:這是一個(gè)很豐富的內(nèi)容,今天就不詳細(xì)說(shuō)明了克伊,留一個(gè)坑酥郭,為以后23種設(shè)計(jì)模式的時(shí)候,再詳細(xì)說(shuō)明答毫!

1褥民、為什么要定義工作單元

首先了解工作單元(Unit of Work)的意圖:維護(hù)受業(yè)務(wù)影響的對(duì)象列表,并且協(xié)調(diào)變化的寫(xiě)入和解決并發(fā)問(wèn)題洗搂。

可以用工作單元來(lái)實(shí)現(xiàn)事務(wù)消返,工作單元就是記錄對(duì)象數(shù)據(jù)變化的對(duì)象载弄。只要開(kāi)始做一些可能對(duì)所要記錄的對(duì)象的數(shù)據(jù)有影響的操作,就會(huì)創(chuàng)建一個(gè)工作單元去記錄這些變化撵颊,所以宇攻,每當(dāng)創(chuàng)建、修改倡勇、或刪除一個(gè)對(duì)象的時(shí)候逞刷,就會(huì)通知工作單元。

2妻熊、如何定義UnitOfWork

1夸浅、在Christ3D.Domain 領(lǐng)域?qū)拥慕涌谖募AInterfaces種,新建工作單元接口 IUnitOfWork.cs

namespace Christ3D.Domain.Interfaces
{ /// <summary>
    /// 工作單元接口 /// </summary>
    public interface IUnitOfWork : IDisposable
    { //是否提交成功
        bool Commit();
    }
}

2扔役、在基礎(chǔ)設(shè)施層帆喇,實(shí)現(xiàn)工作單元接口

namespace Christ3D.Infra.Data.UoW
{ /// <summary>
    /// 工作單元類 /// </summary>
    public class UnitOfWork : IUnitOfWork
    { //數(shù)據(jù)庫(kù)上下文
        private readonly StudyContext _context; //構(gòu)造函數(shù)注入
        public UnitOfWork(StudyContext context)
        {
            _context = context;
        } //上下文提交
        public bool Commit()
        { return _context.SaveChanges() > 0;
        } //手動(dòng)回收
        public void Dispose()
        {
            _context.Dispose();
        }
    }
}

3、記得在IoC層依賴注入

services.AddScoped<IUnitOfWork, UnitOfWork>();

四亿胸、命令處理程序 CommandHandlers

因?yàn)槠ㄌL(zhǎng)了有些暈)和時(shí)間的問(wèn)題坯钦,今天就暫時(shí)先說(shuō)到這里,代碼我已經(jīng)寫(xiě)好了侈玄,并且提交到了Github婉刀,大家如果想看的可以先pull下來(lái),至于為什么這么用以及它的意義序仙,咱們下篇文章再詳細(xì)說(shuō)突颊。其實(shí)整體流程和原理,我在上邊也說(shuō)的很詳細(xì)了诱桂,如果你能根據(jù)聯(lián)合國(guó)的栗子看懂這個(gè)(注意要結(jié)合與依賴注入來(lái)理解)洋丐,那你就是完完全全的理解了,如果下邊的代碼還不是很清楚挥等,沒(méi)關(guān)系奖地,周末大家先看看嘁酿,下周我詳細(xì)給大家講解下。

我這里先給大家列舉下三步走,為下次做準(zhǔn)備:

1济瓢、添加一個(gè)命令處理程序基類 CommandHandler.cs

2涡贱、通過(guò)緩存Memory來(lái)記錄通知信息(錯(cuò)誤方法)

3践付、定義學(xué)生命令處理程序 StudentCommandHandler.cs

五全封、息鼓

今天真沒(méi)想到會(huì)寫(xiě)這么多,看來(lái)還是夜里安靜的時(shí)候更容易寫(xiě)東西榄檬,思路清晰卜范,沒(méi)辦法,我只能把本文拆成兩個(gè)文章了鹿榜。這篇文章我是來(lái)來(lái)回回的刪了寫(xiě)海雪,寫(xiě)了刪锦爵,一個(gè)下午+一個(gè)晚上,大概6個(gè)小時(shí)奥裸,真是很累心的一個(gè)過(guò)程险掀,不過(guò)想想,哪怕有一個(gè)小伙伴能通過(guò)文字學(xué)到東西湾宙,也是極好極開(kāi)心的樟氢,好啦,老張要睡覺(jué)了侠鳄,至于文章的病句埠啃,截圖等,明天再調(diào)整吧畦攘。加油霸妹!

五十电、GitHub & Gitee

https://github.com/anjoy8/ChristDDD

https://gitee.com/laozhangIsPhi/ChristDDD

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末知押,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鹃骂,更是在濱河造成了極大的恐慌台盯,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畏线,死亡現(xiàn)場(chǎng)離奇詭異静盅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)寝殴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門蒿叠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蚣常,你說(shuō)我怎么就攤上這事市咽。” “怎么了抵蚊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵施绎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我贞绳,道長(zhǎng)谷醉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任冈闭,我火速辦了婚禮俱尼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萎攒。我一直安慰自己遇八,他們只是感情好臭猜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著押蚤,像睡著了一般蔑歌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上揽碘,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天次屠,我揣著相機(jī)與錄音,去河邊找鬼雳刺。 笑死劫灶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掖桦。 我是一名探鬼主播本昏,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼枪汪!你這毒婦竟也來(lái)了涌穆?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤雀久,失蹤者是張志新(化名)和其女友劉穎宿稀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赖捌,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡祝沸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了越庇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罩锐。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卤唉,靈堂內(nèi)的尸體忽然破棺而出涩惑,到底是詐尸還是另有隱情,我是刑警寧澤搬味,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布境氢,位于F島的核電站,受9級(jí)特大地震影響碰纬,放射性物質(zhì)發(fā)生泄漏萍聊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一悦析、第九天 我趴在偏房一處隱蔽的房頂上張望寿桨。 院中可真熱鬧,春花似錦、人聲如沸亭螟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)预烙。三九已至墨微,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扁掸,已是汗流浹背翘县。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谴分,地道東北人锈麸。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像牺蹄,于是被迫代替她去往敵國(guó)和親忘伞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容