Application of listener mode in the system-event bus

The listener pattern is a relatively common design pattern.

In everyday development, we are using the event is a kind of accord listener mode function.

On the listener mode also do not understand the students can WinForm to understand the concept of development.

In WinForm mode, the usage rate of events is very high. Each Controller in the form provides a large number of events, such as Click , DoubleClick , Load , Focus, etc.

Why is it designed like this?

Because, when you write a business related control time, you should be prepared only with display -related code to Button , for example.

Writing a Button is concerned with how to draw buttons that match the size, what color, what border, and the position of the word. As this button is pressed What do I need, you're writing Button do not know, to be handed out to deal with.

So use events of the clicked signal is sent out to the outside to deal with.

Will events be used when we write business?

Few people will use the service code in the event , a common data procedure is as follows:

  1. The front desk submits data through Http request
  2. Through the scheduling within the WebApi framework, execute a Method on a Controller
  3. The developer verifies the validity of the submitted data. It may be implemented directly in the Controller , or it may be implemented in the form of AOP, etc.
  4. Hand the data to the service layer for processing
  5. After certain processing, the service layer passes the data to the persistence layer for processing
  6. Persistence layer persists data

From the process point of view, the entire development process is the process of realizing the business from the beginning to the end. Unlike Button , there are business-related and business-independent, which can be separated by events.

But in fact, business and business also need to be separated.

for example

When we delete a user in the system, we generally need to do the following three things

  1. Check if this user can be deleted . For example, whether there are other scenarios that can only be handled by this user.
  2. Delete user data . This may be physical deletion or logical deletion.
  3. Operation after deletion . Clear After deleting the user, it may exist islands of data . For example, the user's personalized configuration, avatar file, personal network disk, etc.

Of the above three steps, only 2. Suitable for completion in the form of UserService .
The other two are not suitable for the following reasons

  • UserService is very likely to be a possible other Service dependent interface, if dependent on other here Service will be cycle-dependent phenomenon
  • When you are developing UserService.Delete (User user) , the rest of the functions must have not been developed. At this time, the developer has no ability to realize the functions in 1. and 2 .
  • Difficult to maintain, you can imagine if you want to maintain the code like the following is big.
public class UserService : IUserService
{
    private readonly SomeService1 someService1;
    private readonly SomeService2 someService2;
    private readonly SomeService3 someService3;

    public UserService(SomeService1 someService1,
                       SomeService2 someService2,
                       SomeService3 someService3)
    {
        this.someService1 = someService1;
        this.someService2 = someService2;
        this.someService3 = someService3;
        // ...
        // ...
        // ...
    }

    public void Delete(User user)
    {
        someService1.CheckCanDeleteUser(user);
        someService2.CheckCanDeleteUser(user);
        someService3.CheckCanDeleteUser(user);
        //...
        //...
        //...
        // you can add more checker here
        // but you should inject the component in the Ctor.

        this.userRepo.Delete(user);

        someService4.CleanUserData(user);
        someService5.CleanUserData(user);
        someService6.CleanUserData(user);

        // ...
        // ...
        // ...
        // you can add more cleaner here
        // but you should inject the component in the Ctor.
    }
}

The code like the above is difficult to maintain, and the number of lines of code must be beyond the scope of humanity.

More importantly, in a real production environment, even the above example can not be done:

  1. Not everyone will have a CheckCanDeleteUser or CleanUserData method when developing SomeServiceX , the name may not be the same, or there is not at all, after all, this is not its business logic category.
  2. Everyone write your own categories SomeServiceX , the system does not know which data in which operation needs its own to cope with time. If every time there is a demand to be made a must do when it SomeServiceX will become very bloated, it might look more like
public class SomeServiceZ : ISomeServiceZ
{
    public void CheckCanDeleteUser(User user){}

    public void CheckCanDeleteEvent(Event @event){}

    public void CheckCanChangeEventDate(Event @event, DateTime newDate);

    public void CheckCanDeleteOrder(Order order);

    public void CheckCanModifyDeliveryDate(Order order, DateTime new DeliveryDate);

    // ...
    // ...

    public void CleanOrderData(Order order);

    public void CleanUserData(User user);

    public void CleanEventData(Event @event);

    //...
    //...
}

It is easy to find, and finally the Service is not in their own service , but in the other operating system in a wide range of services .

We need to event

With events, we only need to write two events Delete and Deleted in UserService.Delete (User user) .

The rest is left to the class that listens to these events.

public class UserService : IUserService
{
    public event EventHandler<UserDeletingEventArgs> Deleting;
    public event EventHandler<UserDeletedEventArgs> Deleted;

    public void Delete(User user)
    {
        this.Deleting?.Invoke(this, new UserDeltingEventArgs(user));
        this.userRepo.Delete(user);
        this.Deleted?.Invoke(this, new UserDeletedEventArgs(user));
    }
}

When we wrote here with joy, the question came again.

Us when , where listening to this event.

In a use of the IOC / DI of Cotnroller in. UserService instance by the constructor obtained.

private readonly IUserService userService;
public UserController(IUserService userService)
{
    this.userService = userService;

    // 不知道谁监听这个事件
    // 如何把所有需要监听这个事件的 Service 都注入进来
    // 那问题依赖没有得到改善
    // this.userService.Deleting +=
}

From this EventHandler provided by the event function and can not solve these problems in large systems.

Event bus

Bus word comes from computer hardware , computer by a number of components, some of the large amount of information exchanged between them.
When the mouse is clicked, the memory , hard disk and display will change.

Obviously, it is impossible twenty-two connected between various devices to listen for these events .

Each device as long as they produce the information sent to a similar large water pipe of the bus in.

Other devices what they want access to these information reprocess their own information.

We can implement this function in our application system based on the same idea.

Event bus in Reface.AppStarter

Reface.AppStarter provides the event bus function out of the box.

Event of the originator , the event 's handlers totally do not need to understand each other who, even does not care whether the other exists.

Instructions

1 Define an event type

Event type is associated sponsors and handlers of the contract .

  • A handler can handle only one type of event
  • A type of event can have multiple or no handler

In Reface.AppStarter define the type of event , just let it inherited from the Event type, its constructor requested party initiating event instance.

public class MyEvent : Reface.EventBus.Event
{
    public string Message { get; private set; }

    public ConsoleStarted(object source, string message) : base(source)
    {
        this.Message = message;
    }
}

In addition to Source , you also define additional attributes to the event handler can get more information.

2 Initiate the event

IEventBus is a tool for initiating events.

Examples thereof has been registered to Reface.AppStarter the IOC / DI container a.

All components created through IOC / DI will inject IEventBus instances themselves . Let's go back to the previous UserService example

[Component]
public class UserService : IUserService
{
    private readonly IEventBus eventBus;

    public UserService(IEventBus eventBus)
    {
        this.eventBus = eventBus;
    }

    public void Delete(User user)
    {
        this.eventBus.Publish(new UserDeletingEvent(this, user));
        this.userRepo.Delete(user);
        this.eventBus.Publish(new UserDeletedEvent(this, user));
    }
}

In the event originator here, we do not care who all need to listen to this event, as long as the release event can be.

Event Bus based event handlers can handle event classes are allocated.

3 Monitor events

Event monitoring is done by IEventListener <T> . The generic T is the event type.

The interface is simple and easy to understand, and only one method needs to be implemented, that is, the content to be processed after listening.

Note : In order for the container in Reface.AppStarter to capture your Listener , you need to add the [Listener] feature to it.

[Listener]
public class CheckUserDeleteByEventModule : IEventListener<UsrDeletingEvent>
{
    // 由于该组件依然是从 Reface.AppStarter 的容器中创建的,所以这里也可以通过构造函数注入接口的实例
    public readonly IEventService eventService;

    public CheckUserDeleteByEventModule(IEventService eventService)
    {
        this.eventService = eventService;
    }

    // 你可以按最小的业务功能拆分你的事件监听器
    // 比如删除用户
    // 你不需要写一事件监听器去检查系统中所有业务单元同否允许删除除用户
    // 你可以按业务单元逐一实现这些检查
    // 对于不能删除的,只要抛出异常即可
    public void Handle(UsrDeletingEvent @event)
    {
        if(doSomeCheck(@event.User))
        {
            throw new CanNotDeleteDataException(typeof(@event.User), @event.User.Id, "your reason");
        }
    }
}

At last

Use Event Bus can significantly reduce the coupling of the system.

When the rising complexity of the system can also use message bus . Their basic reason is the same, but the message bus for distributed and highly concurrent made a lot of optimization.

But in the single application mode under, Reface.AppStarter provided by the event bus function is fully able to meet the demand.

Related Links

Guess you like

Origin www.cnblogs.com/ShimizuShiori/p/12686231.html