Using events to reduce coupling and increase flexibility

February 4th, 2007

Those who know me are aware that I’m a big fan of the service pattern and domain objects (usually with a pinch of ActiveRecord), and I think that the Repository is the most overrated pattern nowadays. So now you know where I came from.

I was asked to disclose more information of something I mentioned lots of times on Castle Project list and also explained on Cuyahoga development mailing list, which is the use of events in order to decrease coupling and as a consequence maximize flexibility and extensibility. This is something that is rarely used even on Java camp, but when it’s used, they create some really cool stuff (wanna an example? Check SEDA and Apache Directory)

The goal is to have main players of your application firing notifications about important things that have just happened on it. On Java you’d use mostly interfaces or some magic with reflection, or embed a scripting engine like Rhino or Jython. On .net you have the option to use plain events, but the other options apply as well.

Now being pragmatic, when this kind of architecture is useful? IMHO it’s useful when you have non-related process that should take place in response to something that happened on the system. Last year I was porting an application that was complex as it had to deal with a legacy database and a new database. The authentication service authenticated the user using the legacy database, and if that passed we have to ensure a compatible token existed on the new database. Was that a task for the authentication service? Not at all (remember Separation of Concerns?).

So when an user successfully logged in, an UserLoggedIn notification was broadcasted. One of the components listening to this notification was the EnsureTokenExists process, which handled the existence of the user on the new database. Henry and Rafael (the guys at Stronghold) have told me about a system they designed that processed trading orders from a variety of data sources and relied on events to attach different processes/validations before committing the trade.

On the system I’m working on, an user that signs in is also translated into an event, along a lot of different notifications. This is specially significant to centralize, for instance, logging, email sending and data replication.

It’s important that you do not rely on event ordering, though. If your application do need to rely on event ordering, then you’d need to build and manage a pipeline. This is a little bit more sophisticated, but easily achievable.

A practical example

Consider a standard e-commerce application. Suppose we have a payment service. Its goal is to perform some business rules and delegate the payment processing to the payment method selected (paypal, credit card, ACH). Nothing more, nothing less.

Now we to send confirmation emails, send failures notices to the site administration and update some statistics based on the payment process result. All of this certainly does not belong on the payment service. My approach is to create a notification for the payment service. You might consider creating just a big notification service for the whole application, but this might lead to graph cycling (you’ve been warned).

We can use a common term to define the source of events (the publisher) and the consumers (the subscribers). The service notification service is the publisher, and we can create three subscribers: PaymentEmailSubscriber, PaymentFailureSubscriber and PaymentStatisticSubscriber.

Here is how the notification service might look like:

public interface IPaymentNotificationService
{
    void FirePaymentSucceeded(IPaymentMethod method, RegisteredUser user);
    void FirePaymentFailed(IPaymentMethod method, RegisteredUser user, Exception ex);

    event PaymentNotificationHandler PaymentSucceeded;
    event PaymentErrorNotificationHandler PaymentFailed;
}

And the subscribers:

public class PaymentEmailSubscriber
{
    public void OnPaymentSucceeded(IPaymentNotificationService notificationService, PaymentNotificationArgs args)
    {
        Console.WriteLine("Sending email to user notifying about successful transaction");
    }
}

public class PaymentFailureSubscriber
{
    public void OnPaymentFailed(IPaymentNotificationService notificationService, PaymentErrorNotificationArgs args)
    {
        Console.WriteLine("Sending email to Admin about transaction failure");
    }
}

public class PaymentStatisticSubscriber
{
    public void OnPaymentSucceeded(IPaymentNotificationService notificationService, PaymentNotificationArgs args)
    {
        Console.WriteLine("Statistics: one more successful payment");
    }

    public void OnPaymentFailed(IPaymentNotificationService notificationService, PaymentErrorNotificationArgs args)
    {
        Console.WriteLine("Statistics: one more failed payment");
    }
}

Finally, the payment service:

public class PaymentService
{
    private readonly IPaymentNotificationService notificationService;

    /// <summary>
    /// Initializes a new instance of the <see cref="PaymentService"/> class.
    /// </summary>
    /// <param name="notificationService">The notification service.</param>
    public PaymentService(IPaymentNotificationService notificationService)
    {
        this.notificationService = notificationService;
    }

    /// <summary>
    /// Process payment
    /// </summary>
    /// <param name="method">The payment method.</param>
    /// <param name="user">The registered user instance.</param>
    public void Pay(IPaymentMethod method, RegisteredUser user)
    {
        try
        {
            if (user.IsBlocked)
            {
                throw new PaymentException("Cannot proceed checkout as the user is blocked");
            }

            if (!method.IsValid())
            {
                throw new PaymentException("Payment information refused by third party. " + 
                                           "Please review the information");
            }

            method.Process();

            notificationService.FirePaymentSucceeded(method, user);
        }
        catch(Exception ex)
        {
            notificationService.FirePaymentFailed(method, user, ex);

            throw;
        }
    }
}

Note how the design got very simple with almost no effort, you can plug subscribers to handle new requirements easily.

You can download the EventSample1 to study it in more detail. Note that I’m not using an IoC container here, just to keep things as simple as possible. The wiring is taking place in the application start up:

public static void Main()
{
    // Configure application (wire everything)

    IPaymentNotificationService notification = new DefaultPaymentNotificationService();

    PaymentEmailSubscriber subscriber1 = new PaymentEmailSubscriber();
    PaymentFailureSubscriber subscriber2 = new PaymentFailureSubscriber();
    PaymentStatisticSubscriber subscriber3 = new PaymentStatisticSubscriber();

    // subscribing

    notification.PaymentSucceeded += new PaymentNotificationHandler(subscriber1.OnPaymentSucceeded);
    notification.PaymentFailed += new PaymentErrorNotificationHandler(subscriber2.OnPaymentFailed);
    notification.PaymentSucceeded += new PaymentNotificationHandler(subscriber3.OnPaymentSucceeded);
    notification.PaymentFailed += new PaymentErrorNotificationHandler(subscriber3.OnPaymentFailed);

    PaymentService paymentService = new PaymentService(notification);

    ...
}

Using Windsor and the Event wiring facility

Windsor Container can simplify the wiring for you if you use the Event wiring facility. In this case the initialization code will be simplified:

WindsorContainer container = new WindsorContainer(new XmlInterpreter(new ConfigResource()));

And the configuration file:

<castle>
  <facilities>

    <facility id="event.wiring" 
          type="Castle.Facilities.EventWiring.EventWiringFacility, Castle.MicroKernel" />

  </facilities>
  
  <components>

    <!-- Subscribers -->

    <component id="email.subscriber" 
           type="EventsSample2.Subscribers.PaymentEmailSubscriber, EventsSample2" />
    <component id="failure.subscriber"
           type="EventsSample2.Subscribers.PaymentFailureSubscriber, EventsSample2" />
    <component id="statistics.subscriber"
           type="EventsSample2.Subscribers.PaymentStatisticSubscriber, EventsSample2" />

    <!-- Notification service -->

    <component id="payment.notification.service"
           service="EventsSample2.IPaymentNotificationService, EventsSample2"
           type="EventsSample2.DefaultPaymentNotificationService, EventsSample2" >
      
      <subscribers>
        <subscriber id="email.subscriber" 
              event="PaymentSucceeded" 
              handler="OnPaymentSucceeded"/>
        
        <subscriber id="failure.subscriber" 
              event="PaymentFailed" 
              handler="OnPaymentFailed"/>
        
        <subscriber id="statistics.subscriber" 
              event="PaymentSucceeded" 
              handler="OnPaymentSucceeded"/>
        <subscriber id="statistics.subscriber" 
              event="PaymentFailed" 
              handler="OnPaymentFailed"/>
      </subscribers>
      
    </component>

    <!-- Services -->

    <component id="payment.service"
           type="EventsSample2.PaymentService, EventsSample2" />

  </components>
</castle>

Again, a sample is available if you want to download it.

Final thoughts

As with any pattern, do not overuse it. Again: do not overuse it. I only rely on it when I’m about to write code that is unrelated to the primary concern of a class. An authentication service should not be concerned about legacy and non-legacy database. That’s the line, and the good judgment shall lead you to the right path.

Hope you enjoyed the reading. Cheers!

4 Responses to “Using events to reduce coupling and increase flexibility”

Steve Says:

Thanks for this post and examples – very useful and practical!

Josh Robb Says:

Fantastic example – thanks! When you say “Repository is the most overrated pattern nowadays” – do you mean that you think there are specific problems with it – or that it’s just overused where other ideas might be better applied? (aka pattern-itis).

hammett Says:

I just think that 90% of people that uses Repositories are using them as DAO, not as Repositories.

Tuna Toksoz Says:

It is quite an old post to reply, but is it possible to attach new component’s methods(subscriber method) to the publisher’s events? I am going to use it in my plugin structure in a way that it can prevent some record to go into db. For example, I have PostService which triggers an event called PostAdding prior to saving the new post. The event args for this event is
public class PostAddingEventArgs:CancellableEventArgs
{
public Post Post{get;} //with c# 3.5 syntax
}
where CancellableEventArgs is
public class CancellableEventArgs:EventArgs
{
public bool Cancel{get;set;} //with c# 3.5 syntax
}

and the code that i use it in is

public void AddPost(Post post)
{
PostAddingEventArgs pevent=new PostAddingEventArgs{Post=post};
if(PostAdding!=null)
this.PostAdding(this, pevent);
if (pevent.Cancel)
return;
_repository.SaveOrUpdate(post);
this.PostAdded(this,new PostAddedEventArgs{Post=post});
}

so any plugin attached to PostAdding event can stop record go into db. My question is how can we attach new subscribers to an existing publisher run time. The use case would be Akismet plugin and some flood controls, for example.

Thanks!

Leave a Reply