ABP入門系列(19)——使用領域事件

ABP入門系列目錄——學習Abp框架之實操演練
源碼路徑:Github-LearningMpaAbp


1.引言

最近剛學習了下DDD中領域事件的理論知識,總的來說領域事件主要有兩個作用,一是解耦,二是使用領域事件進行事務的拆分,通過引入事件存儲,來實現數據的最終一致性。若想了解DDD中領域事件的概念,可參考DDD理論學習系列(9)-- 領域事件

領域事件實現最終一致性

Abp中使用事件總線來實現領域事件,而關于事件總線的實現,大家可參考我這篇博文——事件總線知多少,本文將不再贅述。

2.用例分析

當用戶被成功分配任務后,發送郵件和消息通知給用戶。

這個用例比較簡單,沒有太多的復雜邏輯,按照我們傳統的思路,直接在任務編輯方法中添加郵件和消息發送的方法即可,代碼如下:

public void UpdateTask(UpdateTaskInput input)
{
    //We can use Logger, it's defined in ApplicationService base class.
    Logger.Info("Updating a task for input: " + input);

    //獲取是否有權限
    bool canAssignTaskToOther = PermissionChecker.IsGranted(PermissionNames.Pages_Tasks_AssignPerson);
    //如果任務已經分配且未分配給自己,且不具有分配任務權限,則拋出異常
    if (input.AssignedPersonId.HasValue && input.AssignedPersonId.Value != AbpSession.GetUserId() &&
        !canAssignTaskToOther)
    {
        throw new AbpAuthorizationException("沒有分配任務給他人的權限!");
    }

    var updateTask = Mapper.Map<Task>(input);
    var user = _userRepository.Get(input.AssignedPersonId.Value);
    //先執行分配任務
    _taskManager.AssignTaskToPerson(updateTask, user);

    //再更新其他字段
    _taskRepository.Update(updateTask);

    //發送通知
    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() });
}
更新任務出錯

運行,直接掛掉。原因是很清楚,是由于郵箱配置有誤導致。但是我們思考一下。我們進行任務分配時最關注的是任務被成功分配,而至于通知是否成功發送相對來說是次要的。但是現在卻由于通知發送失敗導致任務無法被成功分配,這是不合理的。

那我們要如何做呢?當然是拆分業務邏輯。而這時領域事件就可以粉墨登場了。

3.使用領域事件

就這個用例而言,“用戶被成功分配任務”就是一個領域事件。下面我們就來實際應用一下。

3.1. 定義事件源

一個領域事件是通過事件源來識別的,我們直接定義一個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. 實現事件處理

定義TaskAssignedToUser事件處理,實現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. 事件觸發

我們可以直接在上一節定義的TaskManager領域服務中觸發領域事件。因為這樣更符合當前領域事件通用語言的表述。

//TaskManager.cs
public void AssignTaskToPerson(Task task, User user)
{
    //已經分配,就不再分配
    if (task.AssignedPersonId.HasValue && task.AssignedPersonId.Value == user.Id)
    {
        return;
    }

    if (task.State != TaskState.Open)
    {
        throw new ApplicationException("處于非活動狀態的任務不能分配!");
    }

    task.AssignedPersonId = user.Id;

    //使用領域事件觸發發送通知操作
    _eventBus.Trigger(new TaskAssignedEventData(task, user));
}

再運行,我們發現雖然沒有接收到消息通知(發送失敗),但任務卻可以成功分配。

4. 一些問題

  1. 領域事件在哪注冊(訂閱)?
    應用程序啟動時Abp根據約定俗成的命名規則將事件源和事件處理注冊到了依賴容器中和事件總線維護的容器中。我們也可以自行在應用服務或領域服務中手動注冊。
  2. 領域事件在哪觸發(發布)?
    事件的觸發同樣也沒有限定,根據需要,可以在應用服務、領域服務、聚合、實體中發布。
  3. 領域事件的命名?
    領域事件的名字要反映出過去發生的事情的概念。

4.最后

由于demo比較簡單,找不到合適的用例,以上使用的用例比較簡單。在復雜的用例中,當需要更新多個聚合時,領域事件的作用就體現出來了,借助領域事件我們可以很好的進行事務拆分,達到最終一致性的目的。

而至于領域事件衍生出來的事件存儲和事件溯源,下次再和大家分享。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容