使用领域事件
1.引言
最近剛學習了下DDD中領(lǐng)域事件的理論知識,總的來說領(lǐng)域事件主要有兩個作用,一是解耦,二是使用領(lǐng)域事件進行事務的拆分,通過引入事件存儲,來實現(xiàn)數(shù)據(jù)的最終一致性。若想了解DDD中領(lǐng)域事件的概念,可參考DDD理論學習系列(9)-- 領(lǐng)域事件。
Abp中使用事件總線來實現(xiàn)領(lǐng)域事件,而關(guān)于事件總線的實現(xiàn),大家可參考我這篇博文——事件總線知多少,本文將不再贅述。
2.用例分析
當用戶被成功分配任務后,發(fā)送郵件和消息通知給用戶。
這個用例比較簡單,沒有太多的復雜邏輯,按照我們傳統(tǒng)的思路,直接在任務編輯方法中添加郵件和消息發(fā)送的方法即可,代碼如下:
public void UpdateTask(UpdateTaskInput input){ ??//We can use Logger, it's defined in ApplicationService base class.Logger.Info("Updating a task for input: " + input); ? ?
?//獲取是否有權(quán)限bool canAssignTaskToOther = PermissionChecker.IsGranted(PermissionNames.Pages_Tasks_AssignPerson); ?
? ?//如果任務已經(jīng)分配且未分配給自己,且不具有分配任務權(quán)限,則拋出異常if (input.AssignedPersonId.HasValue && input.AssignedPersonId.Value != AbpSession.GetUserId() &&!canAssignTaskToOther){ ? ? ? ?throw new AbpAuthorizationException("沒有分配任務給他人的權(quán)限!");} ? ?var updateTask = Mapper.Map<Task>(input); ? ?var user = _userRepository.Get(input.AssignedPersonId.Value); ? ?//先執(zhí)行分配任務_taskManager.AssignTaskToPerson(updateTask, user); ? ?//再更新其他字段_taskRepository.Update(updateTask); ? ?//發(fā)送通知var message = "You hava been assigned one task into your todo list.";_smtpEmailSender.Send("ysjshengjie@qq.com", updateTask.AssignedPerson.EmailAddress, "New Todo item", message);_notificationPublisher.Publish("NewTask", new MessageNotificationData(message), null,NotificationSeverity.Info, new[] { updateTask.AssignedPerson.ToUserIdentifier() }); }
運行,直接掛掉。原因是很清楚,是由于郵箱配置有誤導致。但是我們思考一下。我們進行任務分配時最關(guān)注的是任務被成功分配,而至于通知是否成功發(fā)送相對來說是次要的。但是現(xiàn)在卻由于通知發(fā)送失敗導致任務無非被成功分配,這是不合理的。
那我們要如何做呢?當然是拆分業(yè)務邏輯。而這時領(lǐng)域事件就可以粉墨登場了。
3.使用領(lǐng)域事件
就這個用例而言,“用戶被成功分配任務”就是一個領(lǐng)域事件。下面我們就來實際應用一下。
3.1. 定義事件源
一個領(lǐng)域事件是通過事件源來識別的,我們直接定義一個TaskAssignedEventData繼承自EventData即可:
public class TaskAssignedEventData : EventData{ ?? ?public User User { get; set; } ?
???public Task Task { get; set; } ?
??
?? ?public TaskAssignedEventData(Task task, User user) ? ?{ ?
?? ? ? ? ?this.Task = task; ? ?
?? ? ? ? ?this.User = user;} }
3.2. 實現(xiàn)事件處理
定義TaskAssignedToUser事件處理,實現(xiàn)IEventHandler<TaskAssignedEventData>泛型接口即可:
public class TaskAssignedToUser : IEventHandler<TaskAssignedEventData>, ITransientDependency { ?? ?private readonly ISmtpEmailSender _smtpEmailSender; ?
? ?private readonly INotificationPublisher _notificationPublisher;
? ????public TaskAssignedToUser(ISmtpEmailSender smtpEmailSender, INotificationPublisher notificationPublisher) ? ?{_smtpEmailSender = smtpEmailSender;_notificationPublisher = notificationPublisher;} ? ?public void HandleEvent(TaskAssignedEventData eventData) ? ?{var message = "You hava been assigned one task into your todo list."; ? ? ? ?//TODO:需要重新配置QQ郵箱密碼_smtpEmailSender.Send("ysjshengjie@qq.com", eventData.Task.AssignedPerson.EmailAddress, "New Todo item", message);_notificationPublisher.Publish("NewTask", new MessageNotificationData(message), null,NotificationSeverity.Info, new[] { eventData.User.ToUserIdentifier() });} }
3.3. 事件觸發(fā)
我們可以直接在上一節(jié)定義的TaskManager領(lǐng)域服務中觸發(fā)領(lǐng)域事件。因為這樣更符合當前領(lǐng)域事件通用語言的表述。
//TaskManager.cspublic void AssignTaskToPerson(Task task, User user){ ??//已經(jīng)分配,就不再分配if (task.AssignedPersonId.HasValue && task.AssignedPersonId.Value == user.Id){ ? ? ?
? ?return;} ?
??if (task.State != TaskState.Open){ ? ? ?
???throw new ApplicationException("處于非活動狀態(tài)的任務不能分配!");}task.AssignedPersonId = user.Id; ? ?//使用領(lǐng)域事件觸發(fā)發(fā)送通知操作_eventBus.Trigger(new TaskAssignedEventData(task, user)); }
再運行,我們發(fā)現(xiàn)雖然沒有接收到消息通知(發(fā)送失敗),但任務卻可以成功分配。
4. 一些問題
領(lǐng)域事件在哪注冊(訂閱)?
應用程序啟動時Abp根據(jù)約定俗成的命名規(guī)則將事件源和事件處理注冊到了依賴容器中和事件總線維護的容器中。我們也可以自行在應用服務或領(lǐng)域服務中手動注冊。
領(lǐng)域事件在哪觸發(fā)(發(fā)布)?
事件的觸發(fā)同樣也沒有限定,根據(jù)需要,可以在應用服務、領(lǐng)域服務、聚合、實體中發(fā)布。
領(lǐng)域事件的命名?
領(lǐng)域事件的名字要反映出過去發(fā)生的事情的概念。
4.最后
由于demo比較簡單,找不到合適的用例,以上使用的用例比較簡單。在復雜的用例中,當需要更新多個聚合時,領(lǐng)域事件的作用就體現(xiàn)出來了,借助領(lǐng)域事件我們可以很好的進行事務拆分,達到最終一致性的目的。
而至于領(lǐng)域事件衍生出來的事件存儲和事件溯源,下次再和大家分享。
相關(guān)文章
DDD理論學習系列(1)-- 通用語言
DDD領(lǐng)域驅(qū)動之干貨 (一)
DDD理論學習系列(2)-- 領(lǐng)域
DDD理論學習系列(3)-- 限界上下文
DDD理論學習系列(4)-- 領(lǐng)域模型
事件總線知多少(2)
DDD理論學習系列(5)-- 統(tǒng)一建模語言
DDD理論學習系列(6)-- 實體
DDD理論學習系列(7)-- 值對象
DDD理論學習系列(8)-- 應用服務&領(lǐng)域服務
DDD理論學習系列(9)-- 領(lǐng)域事件
從事件和DDD入手來構(gòu)建微服務
DDD領(lǐng)域驅(qū)動之干貨 (一)
WeText項目:一個基于.NET實現(xiàn)的DDD、CQRS與微服務架構(gòu)的演示案例
【DDD/CQRS/微服務架構(gòu)案例】在Ubuntu 14.04.4 LTS中運行WeText項目的服務端
原文地址:http://www.cnblogs.com/sheng-jie/p/7136279.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
總結(jié)
- 上一篇: 如何利用.NET Core搭建跨平台的控
- 下一篇: ASP.NET Core MVC 控制器