DDD领域驱动设计——领域事件Event

前言 源码地址: 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和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和微服务的思想,其他部分都以最简单的方式实现。请大佬们不要喷我!我已经很努力的写了!呜呜呜呜呜!

您可能有感兴趣的文章
领域驱动设计简介

搜集的一篇关于领域驱动设计(DDD)的理论知识

领域驱动设计的个人理解

从三层架构迈向领域驱动设计

如何实现领域驱动设计