Yesterday we finished some cleanup refactorings around the EventBroker feature of CAB. I'm happy with the result, because it's conceptually simple, it's easy to use, and it isn't much code, yet it helps a lot. I expect some things to be added before release but here is pretty much the core of it as it is right now.

The Challenge

A lot of the CAB features revolve around integrating components in complex, "smart client" apps. A good pattern to integrate components and notifications is to use Pub-Sub (publisher-subscriber)  mechanisms between the services.

Forces
These are some factors influencing the design:

  • Notifications & events one-way are necessary in UI design between visual and non-visual components
  • Publishers & subscribers don’t know about each other
  • You may have sources without handlers and viceversa

CAB Solution

CAB has the following concepts around pub-sub:

Publishers are components that want to raise events, subscribers are components that have methods that are called when events get fired.

Events have an Event Topic. An event topic is the identifier of the event, like "event://Business/Customers/CustomerAdded". The event topic actually also holds a list of WeakReferences to the publisher and subscriber instances, to make sure the event subscription is not the reason why they won't get GC'd.

Events publishers and subscribers may fire off events within a bounded Scope. The scopes CAB allows for are WorkItem-Wide (only components within the WorkItem , and the WorkItem class itself, can get the event) and Global (the default - any component or service anywhere under the Host will get it). Scopes allows you to fire events with the same topic to only interested parties. We evaluated other design options (e.g. get that scope info into the event URI) but they complicated the usage + implemenentation a lot, so we stayed away from that.

Event Subscriptions can be made specifing it for Background so that the subscriber method will be called in a BackgroundWorker thread. This allows you to easily add multithreading into your application without having the caller have explicit knowledge of it. You would use this to access backend web services or work like that, but you wouldn't affect the UI from these other threads. Of course, you need to undestand since this is a one-way pub-sub mechanism, it doesn't make sense to pass the BacgroundWorker to the subscriber since there's no one to flag completion or report progress to (a basic intro to BackgroundWorker here).

Finally, event publications and subscriptions can specify a Strong-typed argument to be passed around. The effect is similar to you using the Whidbey EventHandler<T> feature.

//a publisher class
public class AppleTree
{
   [EventPublication(“event://FallingApples”, EventScope.Global)] 
   event EventHandler<int> ApplesFalling; 
   
   public void MakeApplesFall()
   {
    … 
      //fire the event as a normal event
      if (ApplesFalling!= null) 
      {
         ApplesFalling(this, 4 ) ;
      } 
   }
}
 

//some subscriber class in the same or some other assembly

public class MySubscriber

{

   [EventSubscription(“event://FallingApples”, EventScope.Global )]

   public void ApplesFalling(object sender, int numberOfApples )

   {

      if (numberOfApples > 2 )

      {

         //start picking them up

      } 

   }   

}

 

 

//another subscriber class in the same or some other assembly 

public class OtherSubscriber

{

   [EventSubscription(“event://FallingApples”,EventScope.Global, isBackground=true )]

   public void ThingsOnTheGround(object sender, int numberOfApples )

   {

      //turn on the oven to do an apple pie

      //...this will run in a different thread than the activity

      //that fired the event

   }   

}

   
 

In CAB we have an API style that lets you do everything programatically - good API design and a healthy side-effect of using TDD. The attributes are there for simplifying the dev experience - they get processed whenever an instance of the class that has them gets added to a WorkItem or the Host in CAB. More about WorkItems later.

Related Patterns

Remember my last post - patterns for integrating on the client are conceptually the same you use to integrate multiple services - So here is the pub-sub pattern described by Gregor Hohpe http://patternshare.org/default.aspx/Home.PP.PublishSubscribe, given disclaimers, same concepts apply. Our event broker in this case is a list-based pub-sub (it's not content-based, which would be another way of identifying events - instead of by "topic" and implying type, by type of data being exchanged only). The Mediator [GoF] in this case is the EventTopic object, and we have a Registry [Fowler] that holds the list of these Event Topics.

Those belonging to the Expert Advisor Board for the CAB project of the will be able to see this in action in the next iteration conference call. We are currently working on our packaging of the first drop and you'll see this code if you download it from our GotDotnet workspace, once it's available.

Feedback? ideas? post here or join the new workspace at GotDotNet