.NET Core development actual combat (Lesson 30: Event areas: business to enhance the cohesion and achieve decoupling module) - Study Notes

30 | field events: to enhance the cohesion of business, achieve decoupling module

We abstraction layer defines the interface in the area of ​​field events and field event handling

IDomainEvent

namespace GeekTime.Domain
{
    public interface IDomainEvent : INotification
    {
    }
}

This is an empty interface, which just marked whether a particular object is a field event, INotification is an empty interface, which is an interface MediatR framework is used to achieve with the event delivery

namespace MediatR
{
    public interface INotification
    {
    }
}

Followed by IDomainEventHandler

namespace GeekTime.Domain
{
    public interface IDomainEventHandler<TDomainEvent> : INotificationHandler<TDomainEvent> 
        where TDomainEvent : IDomainEvent
    {
        //这里我们使用了INotificationHandler的Handle方法来作为处理方法的定义
        //Task Handle(TDomainEvent domainEvent, CancellationToken cancellationToken);
    }
}

This interface also inherits the same IDomainEventHandler interface, which has a generic parameter is TDomainEvent, this TDomainEvent constraint must be IDomainEvent, that is to say handler handles only IDomainEvent as to the Senate

In fact, this method has been defined in INotificationHandler Okay, so there is no need to redefine, just to tell you that it is the definition of what it was like

Address areas of event codes in the Entity

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();
}

The field events do come in a property storage entity, it should be a list, because more than one thing can happen in the middle of a process operating entity, the field should be read in the event code outside of the solid model, so exposure to a ReadOnly the Collection

There is also provided a few ways: Add a field event, remove the field events, clear the field events

These methods are invoked inside the domain model

Order can look at the previously defined

public Order(string userId, string userName, int itemCount, Address address)
{
    this.UserId = userId;
    this.UserName = userName;
    this.Address = address;
    this.ItemCount = itemCount;

    this.AddDomainEvent(new OrderCreatedDomainEvent(this));
}

public void ChangeAddress(Address address)
{
    this.Address = address;
    //this.AddDomainEvent(new OrderAddressChangedDomainEvent(this));
}

When we construct a new Order when, in fact, here you can define an event called the OrderCreatedDomainEvent, this field event into its constructor parameter is a Order, when we call the constructor Order, in fact, our behavior is, create a new Order, so here add an event AddDomainEvent

Similarly the example ChangeAddress been called, here we can actually define a OrderAddressChangedDomainEvent field event like this child out

You can see the field of construction and add events should be completed within the domain model method, and should not be outside the code to create the call, because these events are happening inside the domain model

Then look at the definition of OrderCreatedDomainEvent

namespace GeekTime.Domain.Events
{
    public class OrderCreatedDomainEvent : IDomainEvent
    {
        public Order Order { get; private set; }
        public OrderCreatedDomainEvent(Order order)
        {
            this.Order = order;
        }
    }
}

How do we deal with our field events, field events to receive treatment should be defined in the application layer

namespace GeekTime.API.Application.DomainEventHandlers
{
    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));
        }
    }
}

It inherits the IDomainEventHandler, this interface is the interface areas mentioned above event handlers, its generic parameter types OrderCreatedDomainEvent the event is to be processed

For simplicity sake of presentation, the logic here is that when we create a new order, we publish an event to EventBus, this event is called OrderCreated

We define a CreateOrderCommand in the CreateOrder OrderController

[HttpPost]
public async Task<long> CreateOrder([FromBody]CreateOrderCommand cmd)
{
    return await _mediator.Send(cmd, HttpContext.RequestAborted);
}

CreateOrderCommand

namespace GeekTime.API.Application.Commands
{
    public class CreateOrderCommand : IRequest<long>
    {

        //ublic CreateOrderCommand() { }
        public CreateOrderCommand(int itemCount)
        {
            ItemCount = itemCount;
        }

        public long ItemCount { get; private set; }
    }
}

CreateOrderCommandHandler

public async Task<long> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{

    var address = new Address("wen san lu", "hangzhou", "310000");
    var order = new Order("xiaohong1999", "xiaohong", 25, address);

    _orderRepository.Add(order);
    await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
    return order.Id;
}

We created CreateOrderCommandHandler inside an Order, and then save into the warehouse, called UnitOfWork of SaveEntitiesAsync

Start the program, executed directly, call our method, we can see we first entered into creating an order processing system (CreateOrderCommandHandler), then into the Publish code (MediatorExtension) field events released, when storage memory is completed, into the OrderCreatedDomainEventHandler , that is to say after we finish creating our domain model and save it, our field event handler is triggered only

When explaining to achieve UnitOfWork before (EFContext), our SaveEntitiesAsync there is only one line of code is SaveChangesAsync, here to add a line of code, is sent field event code DispatchDomainEventsAsync

public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{
    var result = await base.SaveChangesAsync(cancellationToken);
    //await _mediator.DispatchDomainEventsAsync(this);
    return true;
}

This is MediatorExtension seen DispatchDomainEventsAsync

namespace GeekTime.Infrastructure.Core.Extensions
{
    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);
        }
    }
}

We can see that we send a field event is actually such a process: we want to save the current EntityContext go inside track our entity, then get to the track from the object entity to our current Event, Event exists if , put it out, then the entity Event cleared, and then these Event sent out one by one through the middleware, and the process to find the corresponding Handler

Definition Field events actually very simple, just create a directory Events in the domain model, then the field events are defined here, the field events need to inherit IDomainEvent, processor field events are defined in DomainEventHandler, the application layer in this directory Here, we can define our handler for each event

in conclusion

Create a model in the field of event: We do not go outside the domain model structure of the event, and then passed to the domain model, because the whole field of events is triggered by the business logic of the field, rather than that the operation of the model outside of the trigger

The other is for field events should define specific areas of event handler class, as we have just demonstrated, in a particular directory, defined for each event processing class

Another is in the same transaction which to deal with our field events, in fact, we can also choose to deal with in a separate transaction which, if the time needed to deal with events in different areas of affairs inside, we need to consider consistency question, consider the middle error message loss problems

Creative Commons License

This work is Creative Commons Attribution - NonCommercial - ShareAlike 4.0 International License Agreement for licensing.

Welcome to reprint, use, repost, but be sure to keep the article signed by Zheng Ziming (containing links: http://www.cnblogs.com/MingsonZheng/ ), shall not be used for commercial purposes, be sure to publish the same work based on the paper license modification .

If you have any questions, please contact me ([email protected]).

Guess you like

Origin www.cnblogs.com/MingsonZheng/p/12556333.html