前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ABP入门系列(19)——使用领域事件

ABP入门系列(19)——使用领域事件

作者头像
圣杰
发布2018-01-11 15:38:01
1.2K0
发布2018-01-11 15:38:01
举报
文章被收录于专栏:圣杰的专栏

源码路径:Github-LearningMpaAbp

1.引言

最近刚学习了下DDD中领域事件的理论知识,总的来说领域事件主要有两个作用,一是解耦,二是使用领域事件进行事务的拆分,通过引入事件存储,来实现数据的最终一致性。若想了解DDD中领域事件的概念,可参考DDD理论学习系列(9)-- 领域事件

领域事件实现最终一致性
领域事件实现最终一致性

Abp中使用事件总线来实现领域事件,而关于事件总线的实现,大家可参考我这篇博文——事件总线知多少,本文将不再赘述。

2.用例分析

当用户被成功分配任务后,发送邮件和消息通知给用户。

这个用例比较简单,没有太多的复杂逻辑,按照我们传统的思路,直接在任务编辑方法中添加邮件和消息发送的方法即可,代码如下:

代码语言:javascript
复制
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即可:

代码语言:javascript
复制
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>泛型接口即可:

代码语言:javascript
复制
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领域服务中触发领域事件。因为这样更符合当前领域事件通用语言的表述。

代码语言:javascript
复制
//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比较简单,找不到合适的用例,以上使用的用例比较简单。在复杂的用例中,当需要更新多个聚合时,领域事件的作用就体现出来了,借助领域事件我们可以很好的进行事务拆分,达到最终一致性的目的。

而至于领域事件衍生出来的事件存储和事件溯源,下次再和大家分享。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-07-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.引言
  • 2.用例分析
  • 3.使用领域事件
    • 3.1. 定义事件源
      • 3.2. 实现事件处理
        • 3.3. 事件触发
        • 4. 一些问题
        • 4.最后
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档