前言
源码地址:
https: github com SkylerSkr Skr3D
在前面的文章里,我们会发现一个问题。
通过MediatR发送处理的请求
前言
源码地址:
https://github.com/SkylerSkr/Skr3D
在前面的文章里,我们会发现一个问题。
通过MediatR发送处理的请求,返回值都是void,这等于说变成了发后即忘的状态。在正常开发中,这是不被允许的,至少系统出现异常,要返回结果出去。
而通知的问题,可以归入领域事件Event。
领域事件
以我们的例子,假设领导加了一个需求:创建订单后,需要给用户发送通知短信。
可能你会直接在处理完订单后,直接加上一段SendMessage的代码,但是问题来了,发短信跟处理订单有关系嘛?
发送短信是创建订单必须的功能嘛,显然不是。那么如果以后频繁加入类似短信,邮件或者其他与当前业务无关的代码,那么项目迟早面目全非。所以我们加入了领域事件。
领域事件:对于业务来说,不是必定的,可以变化的业务,是领域事件。
事件抽象类,和实现
/// <summary>
/// 事件模型 抽象基类,继承 INotification
/// 也就是说,拥有中介者模式中的 发布/订阅模式
/// 同时继承了Messgae 也就是继承了 请求/响应模式
/// </summary>
public abstract class Event : INotification
{
// 时间戳
public DateTime Timestamp { get; private set; }
// 每一个事件都是有状态的
protected Event()
{
Timestamp = DateTime.Now;
}
}
public class RegisterOrderEvent : Event
{
}
/// <summary>
/// 领域通知模型,用来获取当前总线中出现的通知信息
/// 继承自领域事件和 INotification(也就意味着可以拥有中介的发布/订阅模式)
/// </summary>
public class DomainNotification : Event
{
// 标识
public Guid DomainNotificationId { get; private set; }
// 键(可以根据这个key,获取当前key下的全部通知信息)
// 这个我们在事件源和事件回溯的时候会用到,伏笔
public string Key { get; private set; }
// 值(与key对应)
public string Value { get; private set; }
// 版本信息
public int Version { get; private set; }
public DomainNotification(string key, string value)
{
DomainNotificationId = Guid.NewGuid();
Version = 1;
Key = key;
Value = value;
}
}
我们用同样的方式写事件的处理程序,实现INotificationHandler
public class OrderEventHandler : INotificationHandler<RegisterOrderEvent>
{
public Task Handle(RegisterOrderEvent notification, CancellationToken cancellationToken)
{
// 恭喜您,注册成功,欢迎加入我们。
return Task.CompletedTask;
}
}
/// <summary>
/// 领域通知处理程序,把所有的通知信息放到事件总线中
/// 继承 INotificationHandler<T>
/// </summary>
public class DomainNotificationHandler : INotificationHandler<DomainNotification>
{
// 通知信息列表
private List<DomainNotification> _notifications;
// 每次访问该处理程序的时候,实例化一个空集合
public DomainNotificationHandler()
{
_notifications = new List<DomainNotification>();
}
// 处理方法,把全部的通知信息,添加到内存里
public Task Handle(DomainNotification message, CancellationToken cancellationToken)
{
_notifications.Add(message);
return Task.CompletedTask;
}
// 获取当前生命周期内的全部通知信息
public virtual List<DomainNotification> GetNotifications()
{
return _notifications;
}
// 判断在当前总线对象周期中,是否存在通知信息
public virtual bool HasNotifications()
{
return GetNotifications().Any();
}
// 手动回收(清空通知)
public void Dispose()
{
_notifications = new List<DomainNotification>();
}
}
依赖注入:
public class NativeInjectorBootStrapper
{
public static void RegisterServices(IServiceCollection services)
{
// ASP.NET HttpContext dependency
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// ASP.NET Authorization Polices
//services.AddSingleton<IAuthorizationHandler, ClaimsRequirementHandler>();
// 注入 应用层Application
services.AddScoped<IOrderAppService, OrderAppService>();
//命令总线Domain Bus(Mediator)
services.AddScoped<IMediatorHandler, InMemoryBus>();
// 领域层 - 领域命令
// 将命令模型和命令处理程序匹配
services.AddScoped<IRequestHandler<RegisterOrderCommand, Unit>, OrderCommandHandler>();
// 领域事件
services.AddScoped<INotificationHandler<RegisterOrderEvent>, OrderEventHandler>();
// 领域通知
services.AddScoped<INotificationHandler<DomainNotification>, DomainNotificationHandler>();
// 领域层 - Memory
services.AddSingleton<IMemoryCache>(factory =>
{
var cache = new MemoryCache(new MemoryCacheOptions());
return cache;
});
// 注入 基础设施层 - 数据层
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<OrderContext>();
}
}
领域层处理完调用事件:
/// <summary>
/// Order命令处理程序
/// 用来处理该Order下的所有命令
/// 注意必须要继承接口IRequestHandler<,>,这样才能实现各个命令的Handle方法
/// </summary>
public class OrderCommandHandler : CommandHandler,
IRequestHandler<RegisterOrderCommand, Unit>
{
// 注入仓储接口
private readonly IOrderRepository _OrderRepository;
// 注入总线
private readonly IMediatorHandler Bus;
/// <summary>
/// 构造函数注入
/// </summary>
/// <param name="OrderRepository"></param>
/// <param name="uow"></param>
/// <param name="bus"></param>
/// <param name="cache"></param>
public OrderCommandHandler(IOrderRepository OrderRepository,
IMediatorHandler bus
) : base( bus)
{
_OrderRepository = OrderRepository;
Bus = bus;
}
// RegisterOrderCommand命令的处理程序
// 整个命令处理程序的核心都在这里
// 不仅包括命令验证的收集,持久化,还有领域事件和通知的添加
public Task<Unit> Handle(RegisterOrderCommand message, CancellationToken cancellationToken)
{
// 实例化领域模型,这里才真正的用到了领域模型
// 注意这里是通过构造函数方法实现
var Order = new Order(Guid.NewGuid(), message.Name, message.Address, message.OrderItem);
//返回错误
if (Order.Name.Equals("Err"))
{
Bus.RaiseEvent(new DomainNotification("", "订单名为Err"));
return Task.FromResult(new Unit());
}
// 持久化
_OrderRepository.Add(Order);
if (_OrderRepository.SaveChanges() > 0)
{
Bus.RaiseEvent(new RegisterOrderEvent());
}
Bus.RaiseEvent(new DomainNotification("", "Register成功") );
return Task.FromResult(new Unit());
}
// 手动回收
public void Dispose()
{
_OrderRepository.Dispose();
}
}
在UI层获取消息:
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
private readonly IOrderAppService _OrderAppService;
private readonly DomainNotificationHandler _notification;
public OrderController(IOrderAppService OrderAppService, INotificationHandler<DomainNotification> notification)
{
_OrderAppService = OrderAppService;
_notification = (DomainNotificationHandler) notification;
}
// POST: Order/Create
// 方法
[HttpPost("Create")]
//[ValidateAntiForgeryToken]
public object Create([FromBody]OrderViewModel OrderViewModel)
{
// 视图模型验证
if (!ModelState.IsValid)
return false;
// 这里为了测试,手动赋值items
OrderViewModel.Items = new List<OrderItemViewModel>() {
new OrderItemViewModel (){
Name="详情"+DateTime.Now
}
};
// 执行添加方法
_OrderAppService.Register(OrderViewModel);
if (_notification.HasNotifications())
{
return _notification.GetNotifications();
}
return true;
}
}
总结:
到这里为止,这个系列就算完成了。此系列重点讲解DDD和微服务的思想,其他部分都以最简单的方式实现。请大佬们不要喷我!我已经很努力的写了!呜呜呜呜呜!