DDD領域驅動設計:領域事件

1 前置閱讀

在閱讀本文章以前,你能夠先閱讀:數據庫

  • DDD領域驅動設計是什麼
  • DDD領域驅動設計:實體、值對象、聚合根
  • DDD領域驅動設計:倉儲
  • MediatR一個優秀的.NET中介者框架

2 什麼是領域事件?

領域事件是在領域中發生的事,你但願同一個領域(進程)的其餘部分了解它。 通知部分一般以某種方式對事件做出反應。框架

3 實現領域事件?

重點強調領域事件發佈/訂閱是使用 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;
    }
}
相關文章
相關標籤/搜索