• DDD领域驱动设计:领域事件


    1 前置阅读

    在阅读本文章之前,你可以先阅读:

    • DDD领域驱动设计是什么
    • DDD领域驱动设计:实体、值对象、聚合根
    • DDD领域驱动设计:仓储
    • MediatR一个优秀的.NET中介者框架

    2 什么是领域事件?

    领域事件是在领域中发生的事,你希望同一个领域(进程)的其他部分了解它。 通知部分通常以某种方式对事件作出反应。

    3 实现领域事件?

    重点强调领域事件发布/订阅是使用 MediatR 同步实现的。

    首先,定义待办事项已更新的领域事件

    public class TodoUpdatedDomainEvent : INotification
    {
        public Todo Todo { get; }
    
        public TodoUpdatedDomainEvent(Todo todo)
        {
            Todo = todo;
        }
    }
    

    然后,引发领域事件,将域事件添加到集合,然后在提交事务之前或之后立即调度这些域事件,而不是立即调度到域事件处理程序 。

    public abstract class Entity
    {
        //...
        private List<INotification> domainEvents;
        public IReadOnlyCollection<INotification> DomainEvents => domainEvents?.AsReadOnly();
    
        public void AddDomainEvent(INotification eventItem)
        {
            domainEvents = domainEvents ?? new List<INotification>();
            domainEvents.Add(eventItem);
        }
    
        public void RemoveDomainEvent(INotification eventItem)
        {
            domainEvents?.Remove(eventItem);
        }
    
        public void ClearDomainEvents()
        {
            domainEvents?.Clear();
        }
        //... 其他代码
    }
    

    要引发事件时,只需将其在聚合根实体的方法处添加到代码中的事件集合。

    public class Todo : AggregateRoot
    {
        //...
        public void Update(
            string name)
        {
            Name = name;
            AddDomainEvent(new TodoUpdatedDomainEvent(this));
        }
        //... 其他代码
    }
    

    请注意 AddDomainEvent 方法的唯一功能是将事件添加到列表。 尚未调度任何事件,尚未调用任何事件处理程序。你需要在稍后将事务提交到数据库时调度事件。

    public class Repository : IDisposable, IRepository
    {
        //...
        private readonly IMediator mediator;
        private readonly DbContext context;
    
        public Repository(DbContext context, IMediator mediator)
        {
            this.context = context ?? throw new ArgumentNullException(nameof(context));
            this.mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
        }
    
        public void Save()
        {
            mediator.DispatchDomainEvents(context);
            context.SaveChanges();
        }
    
        public static void DispatchDomainEvents(this IMediator mediator, DbContext ctx)
        {
            var domainEntities = ctx.ChangeTracker
                .Entries<Entity>()
                .Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any());
    
            var domainEvents = domainEntities
                .SelectMany(x => x.Entity.DomainEvents)
                .ToList();
    
            domainEntities.ToList()
                .ForEach(entity => entity.Entity.ClearDomainEvents());
    
            foreach (var domainEvent in domainEvents)
                mediator.Publish(domainEvent);
        }
        //... 其他代码
    }
    

    最后,订阅并处理领域事件

    public class TodoUpdatedDomainEventHandler : INotificationHandler<TodoUpdatedDomainEvent>
    {
        private readonly ILoggerFactory logger;
        public TodoUpdatedDomainEventHandler(ILoggerFactory logger)
        {
            this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }
    
        public Task Handle(TodoUpdatedDomainEvent todoUpdatedDomainEvent, CancellationToken cancellationToken)
        {
            logger.CreateLogger<TodoUpdatedDomainEvent>().LogDebug("Todo with Id: {TodoId} has been successfully updated",
                    todoUpdatedDomainEvent.Todo.Id);
            return Task.CompletedTask;
        }
    }
    
  • 相关阅读:
    SSH框架——(二)四层结构:DAO,Service,Controller,View层
    Spring知识概括梳理
    设计模式——(一)工厂模式2
    设计模式——(一)工厂模式1
    Spring——(一)IoC
    Toad 实现 SQL 优化
    string 和String的区别
    StructureMap依赖注入
    Oracle/PLSQL: BitAnd Function
    log.debug(e.getMessage());
  • 原文地址:https://www.cnblogs.com/zcqiand/p/14341874.html
Copyright © 2020-2023  润新知