.Net Core 微服务实战 - 领域事件

源码及系列文章目录

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);
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值