在阅读本文章以前,你能够先阅读:数据库
领域事件是在领域中发生的事,你但愿同一个领域(进程)的其余部分了解它。 通知部分一般以某种方式对事件做出反应。框架
重点强调领域事件发布/订阅是使用 MediatR 同步实现的。dom
首先,定义待办事项已更新的领域事件this
public class TodoUpdatedDomainEvent : INotification { public Todo Todo { get; } public TodoUpdatedDomainEvent(Todo todo) { Todo = todo; } }
而后,引起领域事件,将域事件添加到集合,而后在提交事务以前或以后当即调度这些域事件,而不是当即调度到域事件处理程序 。spa
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 方法的惟一功能是将事件添加到列表。 还没有调度任何事件,还没有调用任何事件处理程序。你须要在稍后将事务提交到数据库时调度事件。code
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; } }