開源地址:https://github.com/tangxuehua/enodehtml
上一篇文章,我給你們分享了個人一個基於DDD以及EDA架構的框架enode,可是隻是介紹了一個大概。接下來我準備用不少一篇篇詳細但不冗長的文章介紹每一個點。儘可能爭取一次不介紹太多內容,但但願每次介紹完後都能讓你們知道這個小點的設計思想,以及爲了解決的問題。node
好了,這篇文章,我主要想介紹的是EDA思想在enode框架中如何體現?git
通常的應用程序,若是一個用戶動做會涉及多個聚合根的修改,咱們一般會在應用層服務中建立一個unit of work,而後,咱們可能會設計一個領域服務類,在該領域服務類裏,修改多個聚合根,而後應用層服務將整個unit of work中的修改一次性以事務的方式提交到數據庫。這種方式就是以事務的方式來實現涉及多個聚合根修改的強一致性。以銀行轉帳這個經典的場景做爲分析案例:github
public interface IBankAccountService { void TransferMoney(Guid sourceBankAccountId, Guid targetBankAccountId, double amount); } public class BankAccountService : IBankAccountService { private IContextManager _contextManager; private TransferMoneyService _transferMoneyService; public BankAccountService(IContextManager contextManager, TransferMoneyService transferMoneyService) { _contextManager = contextManager; _transferMoneyService = transferMoneyService; } public void TransferMoney(Guid sourceBankAccountId, Guid targetBankAccountId, double amount) { using (var context = _contextManager.GetContext()) { var sourceAccount = context.Load<BankAccount>(sourceBankAccountId); var targetAccount = context.Load<BankAccount>(targetBankAccountId); _transferMoneyService.TransferMoney(sourceAccount, targetAccount, amount); context.SaveChanges(); } } }
一次銀行轉帳,最核心的動做就是源帳號轉出錢,目標帳號轉入錢;固然實際的銀行轉帳確定不是這麼簡單,也確定不是這麼實現。我拿這個做爲例子只是爲了經過這個你們都熟知的簡單例子來分析若是一個用戶場景涉及不止一個聚合根的修改的時候,若是基於經典的DDD的方式,咱們是如何實現的。如上面的代碼所示,咱們可能會設計一個應用層服務,如上面的IBankAccountService,該應用層服務裏有一個TransferMoney的方法,表示用於實現銀行轉帳的功能;而後該應用層服務會進一步調用一個領域層的轉帳領域服務,就是上面代碼中的TransferMoneyService,按照Eric Evans所說,領域服務應該是一個以動詞命名的服務,一個領域服務能夠明確對應到領域中的一個有業務含義的領域動做,此例就是「轉帳」,因此我設計了一個TransferMoneyService的以動詞來命名的領域服務,該服務的TransferMoney方法實現了銀行轉帳的核心業務邏輯。數據庫
上面這個例子中,按照經典DDD,咱們應該在應用層實現流程控制邏輯以及事務等東西;因此你們能夠看到,以上代碼中,咱們是先獲取一個unit of work,即上面代碼中的context,最後調用context.SaveChanges方法,該方法的職責就是將當前上下文的全部修改以事務的方式提交到數據庫。好了,上面這個例子咱們分析了經典DDD關於如何實現一個會涉及多個聚合根新建或修改的用戶場景;緩存
我一直說enode是一個基於事件驅動架構(EDA,Event-Driven Architecture)的框架。且深藍醫生在前面的回覆中也對什麼是事件驅動的架構有疑惑。因此我想說一下我對事件驅動架構的理解。架構
EDA,顧名思義,我以爲就是事件驅動的,那事件到底驅動了什麼呢?我以爲就是事件驅動狀態的修改。如何理解呢?就是說,假如你要修改一個對象的狀態,那就不是直接調用該對象的某個方法來修改它或者直接經過修改某個對象的屬性來達到修改該對象狀態的目的;取而代之的是,咱們須要先觸發一個事件,而後該對象會響應該事件,而後在響應函數中修改對象本身的狀態。固然,更廣義和權威的事件驅動架構的定義和解釋,我以爲很容易找啊,好比直接去百度上搜一下或直接到wikipedia上搜一下,也很容易就能找到標準的解釋。好比這裏就是我找到的解釋。其實,更大範圍的解釋,就是一種publish-subscriber模式,就是有一個事件生產者產生事件,而後有一個相似event publisher的東西會把這個事件廣播出去,而後全部的事件消費者就能消費該事件了。經過這樣的pub-sub,咱們的應用程序的各個組件之間能夠作到很完全的解耦,而且能夠作到更靈活的擴展性。這兩點的好處應該是很容易體會到的。好比更完全的解耦是,好比原本一個對象要和另外一個對象交互,那它可能要引用該對象,而後調用該對象的某個方法,從而實現對象之間的交互。這種實現方式會讓兩個對象綁定在一塊兒,好比a對象調用b對象的方法,那意味着a須要依賴b對象;而經過事件驅動的方式,a對象只要publish一個事件,而後b對象響應該事件便可,這樣a對象就不知道b對象的存在了,也就是a對象不在依賴b對象;擴展性,就是原本一個事件,可能只有1個事件響應者,可是後面可能因爲功能擴展等緣由,咱們須要增長一個事件響應者,這樣就能方便的作到在不改變原來任何代碼的基礎之上,增長新功能了;其餘的好處就很少分析了,有興趣的能夠再去看看資料吧。併發
上面這一段,我簡單介紹了我所理解的EDA,以及它的基本的好處。下面咱們看看,在enode中,咱們是如何利用EDA這種原理的。爲了簡化,我先用一個簡單的例子說明一下,就用我源代碼中的NoteSample吧,反正也能同樣說明事件驅動的影子在哪裏。看如下的代碼:框架
[Serializable] public class Note : AggregateRoot<Guid>, IEventHandler<NoteCreated>, //訂閱事件 IEventHandler<NoteTitleChanged> { public string Title { get; private set; } public DateTime CreatedTime { get; private set; } public DateTime UpdatedTime { get; private set; } public Note() : base() { } public Note(Guid id, string title) : base(id) { var currentTime = DateTime.Now; //觸發事件 RaiseEvent(new NoteCreated(Id, title, currentTime, currentTime)); } public void ChangeTitle(string title) { //觸發事件 RaiseEvent(new NoteTitleChanged(Id, title, DateTime.Now)); } //事件響應函數 void IEventHandler<NoteCreated>.Handle(NoteCreated evnt) { //在響應函數中修改本身的狀態,這裏能夠體現出EDA的影子,就是事件驅動狀態的修改 Title = evnt.Title; CreatedTime = evnt.CreatedTime; UpdatedTime = evnt.UpdatedTime; } //事件響應函數 void IEventHandler<NoteTitleChanged>.Handle(NoteTitleChanged evnt) { //同上解釋 Title = evnt.Title; UpdatedTime = evnt.UpdatedTime; } }
上面的例子中,Note是一個聚合根,它會響應兩個事件:NoteCreated, NoteTitleChanged。要實現事件響應,咱們能夠經過實現框架提供的IEventHandler<T>接口,就能告訴框架,我要訂閱什麼事件了。分佈式
上面代碼中,應該比較詳細的註釋了每段代碼的含義了,應該都能看懂吧。上面這個例子說明了,聚合跟本身的狀態不是在public方法中直接改的,而是基於事件驅動的方式來修改的,因此,你們能夠看到,聚合根狀態的修改是在一個內部響應函數中修改的。下面咱們再來看一下外部其餘對象,如何響應該事件:
//這是一個事件訂閱者,它也響應了Note的兩個事件 public class NoteEventHandler : IEventHandler<NoteCreated>, IEventHandler<NoteTitleChanged> { public void Handle(NoteCreated evnt) { //這裏爲了簡單,因此只是輸出了一串文字,實際咱們能夠在這裏作任何你想作的事情; Console.WriteLine(string.Format("Note created, title:{0}", evnt.Title)); } public void Handle(NoteTitleChanged evnt) { Console.WriteLine(string.Format("Note title changed, title:{0}", evnt.Title)); } }
經過上面兩個簡單的例子,不知道有沒有解釋清楚,在enode框架中,如何體現EDA?
總結:
我之因此比較喜歡事件驅動這種思想是基於如下理由: