Implementing an Event Driven Message Broker

An entry about arcitechture | separation of concerns Publication date 21. December 2008 20:03

Writing loosely coupled code which separates concerns is not always a simple matter of dependency inversion. Consider the following code, which shows a service responsible for creating a new user account for a website:

public class UserCreator : ICreateUsers
{
    private readonly IRepository<IUser> _repository;
    private readonly IGeneratePasswords _passwords;
    private readonly ISendCredentialsByEmail _emailSender;
 
    public UserCreator(IRepository<IUser> repository, IGeneratePasswords passwords, 
                       ISendCredentialsByEmail emailSender)
    {
        _repository = repository;
        _passwords = passwords;
        _emailSender = emailSender;
    }
 
    public IUser Create(string username, string email)
    {
        string password = _passwords.Generate();
 
        Membership.CreateUser(username, password, email);
        IUser user = _repository.FindSingle(new UserByUsernameSpecification(username));
 
        _emailSender.Send(user.Username, password, user.Email);
 
        return user;
    }
}

Do you notice any concern which is bleeding into this code, even though the actual implementation of it is separated?

When an anonymous user wishes to create an account at our site, we ask for the desired username and their email address. Upon creating the user, we auto-generate the password and send it to the given email address, enabling the user to login once he receives the email (this is so that we can verify that the new user gave us a real email address).

Apart from the concern of sending such emails bleeding into our user creation service, we’re also introducing both a possible performance issue and a  possible crash site - trying to send the email might take a long time, and the email sender might throw and exception our way if something goes wrong. Sending the email is not vital to the concern of creating the user (there’s no way we can guarantee that it gets delivered anyways), so a failure there shouldn’t cause everything else to crash.

Sure, we can get around both of these issues by rewriting our Create method a bit:

public IUser Create(string username, string email)
{
    string password = _passwords.Generate();
 
    Membership.CreateUser(username, password, email);
    IUser user = _repository.FindSingle(new UserByUsernameSpecification(username));
 
    ThreadPool.QueueUserWorkItem(m => _emailSender.Send(username, password, email));
    
    return user;
}

I don’t know about you, but that doesn’t look very elegant to me – now we’re introducing infrastructure concerns into our service. Besides, this is a very simple example – in more advanced scenarios this kind of stuff could begin to look very ugly very fast: What happens when we start to introduce more requirements, for instance? Say that whenever a user is created, a task should be added to the Administrators task list reminding him to verify and set up the any role memberships required for the new user. This would require us to further dilute the UserCreator:

public class UserCreator : ICreateUsers
{
    private readonly IRepository<IUser> _repository;
    private readonly IGeneratePasswords _passwords;
    private readonly ISendCredentialsByEmail _emailSender;
    private readonly ICreateAdminTasks _adminTaskCreator;
 
    public UserCreator(IRepository<IUser> repository, IGeneratePasswords passwords,
                       ISendCredentialsByEmail emailSender, IAdminTasksService adminTaskCreator)
    {
        _repository = repository;
        _passwords = passwords;
        _emailSender = emailSender;
        _adminTaskCreator = adminTaskCreator;
    }
 
    public IUser Create(string username, string email)
    {
        string password = _passwords.Generate();
 
        Membership.CreateUser(username, password, email);
        IUser user = _repository.FindSingle(new UserByUsernameSpecification(username));
 
        ThreadPool.QueueUserWorkItem(m => _emailSender.Send(username, password, email));
        ThreadPool.QueueUserWorkItem(m => _adminTasks.AddTask(AdminTaskType.NewUserCreated, user));
        
        return user;
    }
    
}

At this point, the UserCreator class actually contains nearly half as much code just doing concern delegation as it is actually solving what it itself is concerned with (creating the user!). What we need, is a system where we can separate not only the concerns, but the flow between them.

An Event-Driven Architecture

One way to solve this, is to implement an event-driven architecture. What that means, is basically that we enable our services to either publish or consume events which are defined within the domain model. For instance, imagine if our UserCreator published an event whenever it created a new user – then the services which need to take some action whenever this happens merely need to somehow consume this event. Of course, this requires a bit of infrastructure to get working. There are several frameworks out there which offer this functionality – most notably there’s NServiceBus, and the up-and-coming Mass Transit. However, both of these are what we call ESBs – Enterprise Service Buses. They’re probably great if you’re working on distributed applications, but personally I feel they’re a bit over the top for what I’d normally need – a simple system for messaging within the bounds of a single application. In such a scenario, I think that bog standard .NET delegates (events) will do just fine.

Rolling My Own

As a learning exercise, I decided to try rolling my own event-driven architecture infrastructure. Using it, here’s what my improved UserCreator looks like:

public class UserCreator : ICreateUsers, IPublisherOf<NewUserCreatedMessage>
{
    private readonly IRepository<IUser> _repository;
    private readonly IGeneratePasswords _passwords;
 
    public UserCreator(IRepository<IUser> repository, IGeneratePasswords passwords)
    {
        _repository = repository;
        _passwords = passwords;
    }
 
    public IUser Create(string username, string email)
    {
        string password = _passwords.Generate();
 
        Membership.CreateUser(username, password, email);
        IUser user = _repository.FindSingle(new UserByUsernameSpecification(username));
 
        Publish(new NewUserCreatedMessage(user, password));
 
        return user;
    }
    
    public event MessageEventHandler<NewUserCreatedMessage> Publish = m => { };
}

Notice how it implements the IPublisherOf interface, which is where the Publish event is coming from. Whenever the class wants to publish a message, it simply needs to raise this event. On the consumer end, I can then implement a subscriber of NewUserCreatedMessages:

public class SendNewUserCredentialsEndpoint : IConsumerOf<NewUserCreatedMessage>
{
    private ISendCredentialsByEmail _emailSender;
 
    public SendNewUserCredentialsEndpoint(ISendCredentialsByEmail emailSender)
    {
        _emailSender = emailSender;
    }
 
    public void Consume(NewUserCreatedMessage message)
    {
        _emailSender.Send(message.UserName, message.UserPassword, message.UserEmail);
    }
}

Here, I’m using the IConsumerOf interface to signal that this class wants to be notified whenever a NewUserCreatedMessage is published anywhere. And as easy as that, I’ve completely decoupled the concern of creating users from the concern of sending new users an email with their password.

How Does it Work?

To get this working, there has to be something that listens for any events that are published and forwards them to the appropriate consumers – I call this a message broker. Basically, this is the “thing” which will know about all the current publishers and consumers and how they wire together. This is where having an IOC container in charge of all the components in my application came in really, really handy, because it allowed me to implement an integration between my message broker and the container so that publishers and subscribers could be automatically discovered. All that needs to happen to make this work, is to install the MessageBrokerModule as the first module in the kernel (so that it can monitor bindings to find any consumers, and activations to find any publishers):

var kernel = new StandardKernel(
    new MessageBrokerModule(),
    new ControllerModule(typeof(PageController).Assembly),
    new ServicesModule())

With this module, whenever a binding for a component which implements the IConsumerOf interface is added to the kernel, a subscription for it will be added to the message broker. Whenever a component which implements the IPublisherOf interface is activated through the kernel, a handler for its Publish event will be created so that any messages published by it will be passed to the message broker. The fact that subscriptions are created for the bindings of consumers, means that even consumers which haven’t yet been activated (instantiated) will be so just in time to receive any messages they have requested notification of. I’m not sure if this is a common feature of an event-driven architecture (I suspect not), but it is something that I’ve found to be very useful because it makes non-singleton consumers “just work”.

Download

If you want to have a look at the code for my message broker API, you can download it here, complete with a simple example and a suite of tests. For the time being I’ve only implemented an integration with Ninject for it, but making it work with other containers shouldn’t be too hard.

The Oxitis Disclaimer: Please note that I am by no means an expert on event-driven architecture, so the message broker I’ve implemented might very well not be how the pro’s would do it.

Currently rated 4.0 by 2 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS