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

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

ABP入门系列(19)——使用领域事件的更多相关文章

  1. ABP入门系列(3)——领域层创建实体

    这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进行一一对应.网上有代码生成器去简化我们这一步的任务,但是不建议初学者去使用. 一.首先来看看ABP体系结构 领域层就 ...

  2. ABP入门系列(18)—— 使用领域服务

    ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1.引言 自上次更新有一个多月了,发现越往下写,越不知如何去写.特别是当遇到DDD中一些概念术语的 ...

  3. ABP入门系列(2)——领域层创建实体

    ABP入门系列目录--学习Abp框架之实操演练 这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进行一一对应.网上有代码生成器去简化我们这一步的任务,但是不建议初学者 ...

  4. ABP入门系列(4)——领域层定义仓储并实现

    一.先来介绍下仓储 仓储(Repository): 仓储用来操作数据库进行数据存取.仓储接口在领域层定义,而仓储的实现类应该写在基础设施层. 在ABP中,仓储类要实现IRepository接口,接口定 ...

  5. ABP入门系列(3)——领域层定义仓储并实现

    ABP入门系列目录--学习Abp框架之实操演练 一.先来介绍下仓储 仓储(Repository): 仓储用来操作数据库进行数据存取.仓储接口在领域层定义,而仓储的实现类应该写在基础设施层. 在ABP中 ...

  6. ABP入门系列目录——学习Abp框架之实操演练

    ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WE ...

  7. ABP入门系列(1)——学习Abp框架之实操演练

    作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...

  8. ABP入门系列(6)——展现层实现增删改查

    这一章节将通过完善Controller.View.ViewModel,来实现展现层的增删改查.最终实现效果如下图: 一.定义Controller ABP对ASP.NET MVC Controllers ...

  9. ABP入门系列(5)——创建应用服务

    一.解释下应用服务层 应用服务用于将领域(业务)逻辑暴露给展现层.展现层通过传入DTO(数据传输对象)参数来调用应用服务,而应用服务通过领域对象来执行相应的业务逻辑并且将DTO返回给展现层.因此,展现 ...

随机推荐

  1. springcloud(五):熔断监控Hystrix Dashboard和Turbine

    Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数 ...

  2. c++ 自动应用类型转换

    c++中,在赋值时如果类型不匹配,就会应用到:类型转换.类型转换又分为隐式转换(implicit conversion) 和 显式强制类型转换(emplcit conversion).在这我围绕着类的 ...

  3. PHP中array_merge函数与array+array的区别

    在PHP中可以使用array_merge函数和两个数组相加array+array的方式进行数组合并,但两者效果并不相同,下面为大家介绍两者具体的使用区别. 区别如下: 当下标为数值时,array_me ...

  4. zabbix server安装详解

    简介 zabbix(音同 zæbix)是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以 ...

  5. 项目管理之 使用 appledoc 生成开发文档

    写项目时通常会遇到要求写开发文档的需求,但是就源代码来说,文档最好和源码在一起,这样更新起来更加方便和顺手.Objective-C 有一些文档管理工具,doxygen, headdoc 和 apple ...

  6. C++函数调用的反汇编过程及Thunk应用

    x86汇编基础知识 1. 汇编常用寄存器 esp,(Extended stack pointer)栈顶指针.因为x86的栈内存是向下扩展的,因此当push入栈时,esp–.pop出栈时,esp++.e ...

  7. 逆向libbaiduprotect(四)

    百度加固libbaiduprotect.so自身对只读字符串进行了加密保护,防止成为破解和逆向的切入口.一般地认为,只要找出这个解密算法就可以对.rodata段的只读字符串进行破解,从而窥探程序的意图 ...

  8. 谈谈this对象

    通过平常的使用简单总结了一下不同形式的函数调用下this的指向,函数的调用形式决定了this的指向.就简单分析一下以下几种情况: 情况一:纯粹的函数调用 eg: var x=1; function f ...

  9. java 中变量存储位置的区别

    1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.  2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字 ...

  10. JS中函数参数值传递和引用传递

    也许大家对于函数的参数都不会太在意,简单来说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样.深入研究,你会发现其实没那么简单,这个传参是要分俩种情况(其实这是个错误的说法 ...