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

1 前置閱讀

在閱讀本文章之前,你可以先閱讀:

什麼是DDD

DDD的實體、值物件、聚合根的基類和介面:設計與實現

DDD的倉儲(Repository):設計與實現

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 domainEvents; public IReadOnlyCollection DomainEvents => domainEvents?。AsReadOnly(); public void AddDomainEvent(INotification eventItem) { domainEvents = domainEvents ?? new List(); 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() 。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{ 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()。LogDebug(“Todo with Id: {TodoId} has been successfully updated”, todoUpdatedDomainEvent。Todo。Id); return Task。CompletedTask; }}