Quantcast
Channel: try-catch-FAIL - .NET
Viewing all articles
Browse latest Browse all 14

Using An Application Bus To Raise Events

$
0
0

I’ve been writing recently about the Application Bus pattern and how it’s used in RageFeed.  So far, I’ve shown you how it can be used to send both one-way commands as well as synchronous request-reply commands, enabling you to decouple the pieces of your application.  There’s another type of communication that is even more loosely coupled, and it’s a good fit for scenarios where you want to perform a variety of unrelated actions in response to something occurring in your application.  In this article, I’ll show you how to use the Application Bus to raise and handle events.

The Problem: A Violation Of Principles

Let’s look at a somewhat contrived example.  Suppose in RageFeed, when a new user registers, we need to create the user’s account, send the user a “Welcome” E-mail, send ourselves an E-mail, and send the user a free basket of goodies.  Here’s a possible implementation:

public class CreateUserHandler : IHandle<CreateUserRequest, CreateUserReply>
{
    ...

    public CreateUserReply Handle(CreateUserRequest request)
    {
        var user = new User {Username = request.Username, Email = request.Email};
        user.SetPassword(request.Password);

        _repository.Add(user);

        _dispatcher.SendNewUserActivation(request.Username, request.Email);
        
        _dispatcher.NotifyAdminsOfNewUserRegistration(request.Username);

        _fruitService.SendUserWelcomeBaseket(user);

        return new CreateUserReply {Succeeded = true};
    }
}

Even though we’re using Dependency Injection and leveraging the bus, we’ve still managed to violate several design principles in implementing this requirement. 

Single Responsibility Principle

The Single Responsibility Principle (SRP) states that a class should have exactly one reason to change.   In this case, our command handler actually could change for a lot of reasons.  The class would need to change if we decided to stop sending gift baskets, if we decide we don’t want to be notified when new users join, or if we decide we want to perform additional actions after a user registers.  And that leads us to our next violation…

The Open/Closed Principle

The Open/Closed Principle (OCP) states that classes should be closed for modification, but open for extension.  This means we should be able to add new functionality to an existing class without actually editing the class.  But how can we do that here?? One option would be to make the Handle method virtual, then derive a new class that adds new behavior, but that’s not a clean solution (remember, favor composition over inheritance, meaning look for ways to combine multiple independent classes rather than creating complicated inheritance hierarchies).

So, what can we do?  We need to perform several different actions after a user registers.  We know those actions may change in the future.  What can we do to support our requirements while not violating SRP or OCP?  The answer is simple: events! 

The Solution: Events

Let’s re-examine what our command handler should really be doing.  It should be performing the core “register user” action, and that’s it.  In this case, that means adding the user to the underlying repository.  All the other responsibilities are not part of our core process: the user is still registered even if we don’t perform them.  So,those auxiliary responsibilities are good candidates to refactor.  First, let’s add a new IRaiseEvents interface that our command handler can use to signal interesting events:

public interface IRaiseEvents
{
    void Raise<TEvent>(TEvent @event);
}

Next, let’s utilize this to signal that we’ve added a new user:

public CreateUserReply Handle(CreateUserRequest request)
{
    var user = new User {Username = request.Username, Email = request.Email};
    user.SetPassword(request.Password);

    _repository.Add(user);

    //_events is IRaiseEvents, injected by the IoC container. 
    _events.Raise(new UserRegisteredEvent {User = user});

    return new CreateUserReply {Succeeded = true};
}

Now, let’s add some event handlers to take over the responsibilities that used to live in our command handler:

public class UserActivationEmailSender : IHandle<UserRegisteredEvent>
{
    private readonly IEmailDispatcher _dispatcher;

    public UserActivationEmailSender(IEmailDispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    }

    public void Handle(UserRegisteredEvent message)
    {
        _dispatcher.SendNewUserActivation(message.User.Username, message.User.Email);
    }
}

public class AdminNotifier : IHandle<UserRegisteredEvent>
{
    private readonly IEmailDispatcher _dispatcher;

    public AdminNotifier(IEmailDispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    }

    public void Handle(UserRegisteredEvent message)
    {
        _dispatcher.NotifyAdminsOfNewUserRegistration(message.User.Username);
    }
}

public class WelcomeBaseketSender : IHandle<UserRegisteredEvent>
{
    private ISendFruit _fruitService;

    public WelcomeBaseketSender(ISendFruit fruitService)
    {
        _fruitService = fruitService;
    }

    public void Handle(UserRegisteredEvent message)
    {
        _fruitService.SendUserWelcomeBaseket(message.User);
    }
}

Now we can change what happens when a user registers without having to change our user registration command handler! Another nice benefit is that each action is now a lot easier to test and verify.

Extending the Application Bus With Support For Events

Our IRaiseEvents interface is implemented by the application bus.  We only need to make a few small changes to our existing code.  First, the bus:

public class ApplicationBus : IBus, IRaiseEvents
{
    ...

    public void Raise<TEvent>(TEvent @event)
    {
        var handlers = _registry.GetAllHandlersFor<TEvent>();

        foreach (var handler in handlers)
        {
            try
            {
                handler.Handle(@event);
            }
            catch (Exception ex)
            {
                //TODO: Log this somewhere, but don't rethrow.  The caller doesn't care!
            }
        }
    }
}

Next the registry:

public class MessageHandlerRegistry : IMessageHandlerRegistry
{

    public IEnumerable<IHandle<TMessage>> GetAllHandlersFor<TMessage>()
    {
        try
        {
            return _container.GetAllInstances<IHandle<TMessage>>();
        }
        catch (StructureMapException ex)
        {
            //202 means no suitable type found in the container. 
            if (ex.ErrorCode == 202)
            {
                return new IHandle<TMessage>[0];
            }
            throw;
        }
    }
}

It utilizes the same StructureMap-based conventions for registering event handlers, so we don’t have to make any changes there.  Our event handlers are registered automagically just like our command handlers!

Events or Commands?

Commands and events are handled very similarly with an application bus, so how do you decide if something should be a command or an event?  A command implies that exactly one handler will receive the command and do something with it.  In RageFeed, if you send a command and no one handles it, it’s an error.  Conversely, an event may not be handled at all.  It is handled by zero or more handlers, and the source of the event doesn’t care: it raises the event, then goes on about it’s business.  Events could be implemented asynchronously, even in an application bus, by spawning background threads for each event handler.  If you have event handlers that take a while to execute, this is something you should consider, and it’s an easy change to make should the need arise later.

This is a pretty barebones implementation of events.  If you wanted to go a little further, I’d recommend adding a base marker interface for your event classes to implement as well as your commands (on the grounds of making things explicit).   The error handling is also very primitive.  If an event handler throws an exception, the calling class doesn’t care, but you would definitely want to capture that information somewhere.


I think that covers it for events!  Use them when you want multiple handlers to perform actions that are not related to a core operation.  Like commands, they’re a great tool for creating loosely-coupled, easy-to-maintain code!


Viewing all articles
Browse latest Browse all 14

Trending Articles