领域事件
源码及系列文章目录
Git 源码 :https://github.com/tangsong1995/TS.Microservices
CSDN 资源 :https://download.csdn.net/download/qq_33649351/34675095
系列文章目录 :https://blog.csdn.net/qq_33649351/article/details/120998558
领域事件的定义
领域事件是一个领域模型中极其重要的部分,用来表示领域中发生的事件。忽略不相关的领域活动,同时明确领域专家要跟踪或希望被通知的事情,或与其他模型对象中的状态更改相关联。
比如一个订单系统,下单成功之后,后续需要更新订单状态为支付成功,扣减库存,增加积分等。“订单支付成功”就是一个领域事件。
领域事件的实现
定义领域事件接口
public interface IDomainEvent : INotification
{
}
领域事件接口 IDomainEvent 继承了 MediatR 框架的 INotification 接口,用于实现事件传递。
定义领域事件处理接口
public interface IDomainEventHandler<TDomainEvent> : INotificationHandler<TDomainEvent>
where TDomainEvent : IDomainEvent
{
}
领域事件处理接口 IDomainEventHandler 继承了 MediatR 框架的 INotificationHandler 接口,INotificationHandler接口定义了 Handle 方法,借助此方法实现领域事件的处理。
在 Entity 中实现领域事件的处理
public abstract class Entity : IEntity
{
public abstract object GetKey();
public override string ToString()
{
return $"[Entity: {GetType().Name}] Keys = {string.Join(",", GetKey())}";
}
#region 领域事件的处理
private List<IDomainEvent> _domainEvents;
public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents?.AsReadOnly();
public void AddDomainEvent(IDomainEvent eventItem)
{
_domainEvents = _domainEvents ?? new List<IDomainEvent>();
_domainEvents.Add(eventItem);
}
public void RemoveDomainEvent(IDomainEvent eventItem)
{
_domainEvents?.Remove(eventItem);
}
public void ClearDomainEvents()
{
_domainEvents?.Clear();
}
#endregion
}
Entity 定义了 List<IDomainEvent> 属性用于存储领域事件,并且定义了 IReadOnlyCollection<IDomainEvent> 属性对外暴露只读的领域事件,定义了 *AddDomainEvent 方法用于添加领域事件,定义了 RemoveDomainEvent 方法用于移除领域事件,定义了 ClearDomainEvents 方法用于清理领域事件。
在 EFContext 中实现领域事件的发送
定义 DispatchDomainEventsAsync 领域事件发送方法,实现对领域模型中的所有领域事件的发送:
static class MediatorExtension
{
public static async Task DispatchDomainEventsAsync(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)
await mediator.Publish(domainEvent);
}
}
在 EFContext 的SaveEntitiesAsync方法中实现领域事件的发送:
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{
var result = await base.SaveChangesAsync(cancellationToken);
//实现领域事件的发送
await _mediator.DispatchDomainEventsAsync(this);
return true;
}
实现领域事件
领域事件都是领域模型内部发生的事件,所以领域事件的添加应该在领域模型的构造方法内完成,不应该被外界的方法调用而添加。
定义 OrderCreatedDomainEvent 继承 IDomainEvent :
public class OrderCreatedDomainEvent : IDomainEvent
{
public Order Order { get; private set; }
public OrderCreatedDomainEvent(Order order)
{
this.Order = order;
}
}
在定义领域模型时,添加领域事件:
public Order(string userId, string userName, int itemCount, Address address)
{
this.UserId = userId;
this.UserName = userName;
this.Address = address;
this.ItemCount = itemCount;
//添加OrderCreatedDomainEvent领域事件
this.AddDomainEvent(new OrderCreatedDomainEvent(this));
}
在应用层实现 DomainEventHandler :
public class OrderCreatedDomainEventHandler : IDomainEventHandler<OrderCreatedDomainEvent>
{
ICapPublisher _capPublisher;
public OrderCreatedDomainEventHandler(ICapPublisher capPublisher)
{
_capPublisher = capPublisher;
}
public async Task Handle(OrderCreatedDomainEvent notification, CancellationToken cancellationToken)
{
await _capPublisher.PublishAsync("OrderCreated", new OrderCreatedIntegrationEvent(notification.Order.Id));
}
}
定义 CreateOrderCommandHandler :
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, long>
{
IOrderRepository _orderRepository;
ICapPublisher _capPublisher;
public CreateOrderCommandHandler(IOrderRepository orderRepository, ICapPublisher capPublisher)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
}
public async Task<long> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
var address = new Address("han guo zhong xin", "shenzhen", "000000");
var order = new Order("ts", "tangsong", request.ItemCount, address);
_orderRepository.Add(order);
await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
return order.Id;
}
}
CreateOrderCommand 的定义:
public class CreateOrderCommand : IRequest<long>
{
public CreateOrderCommand(int itemCount)
{
ItemCount = itemCount;
}
public int ItemCount { get; private set; }
}
Controller 定义Api :
public class OrderController : ControllerBase
{
IMediator _mediator;
public OrderController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<long> CreateOrder([FromBody] CreateOrderCommand cmd)
{
return await _mediator.Send(cmd, HttpContext.RequestAborted);
}
}
定义领域事件的注意点
- 由领域模型内部创建事件
- 由专有的领域事件处理类处理领域事件
- 根据实际情况来决定是否在同一事务中处理(如一致性、性能等因素)
更好的定义Api
一般情况下的Api定义
一般情况下的Api定义如下:
[HttpPost]
public Task<long> CreateOrder([FromBody] CreateOrderVeiwModel viewModel)
{
var model = viewModel.ToModel();
return await orderService.CreateOrder(model);
}
class OrderService : IOrderService
{
public long CreateOrder(CreateOrderModel model)
{
var address = new Address("han guo zhong xin", "shenzhen", "000000");
var order = new Order("ts", "tangsong", request.ItemCount, address);
_orderRepository.Add(order);
await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
return order.Id;
}
}
一般情况下的Api定义,Api方法负责模型的转换及服务的调用。当业务逐渐复杂时,Controller会逐渐显出臃肿。
DDD理念下的Api定义
DDD理念下的Api定义,Controller负责与前端的交互,定义输入输出,实现授权、身份认证、路由定义等功能,不负责处理领域模型。
借助 MediatR ,使用中间者模式,将命令发送出去:
[HttpPost]
public async Task<long> CreateOrder([FromBody] CreateOrderCommand cmd)
{
return await _mediator.Send(cmd, HttpContext.RequestAborted);
}