Message Bus – Part 1

I thought this time I might discuss my NUGET package for messaging. The package is called CG.Bus and, as always, I’ll post links for the source and bits at the end of the article. The package exposes a type called IBus, which looks like this:

 

Looking at the source a couple of things are immediately apparent. First, the interface derives from IBuilderProduct, which is part of my builder library, so we know that IBus objects will be created through a builder object. Second there are only two methods on the interface, one for receiving messages, and another for sending messages. That means an IBus is a simplified representation of a messaging architecture and isn’t intended to expose all the complexities and capabilities of any 3rd party messaging package. All we’re after, with an IBus, is the ability to send messages, or receive them, and not be tied to a specific 3rd party messaging package in the process.

So, that simplification approach is one that I’ve taken before, in other NUGET packages. It works for my needs, but, you should carefully consider if it will work for your needs before you try to incorporate my IBus component into your next project.

Now that I’ve shown what an IBus object is, and what it can do, let’s talk about some of the supporting types that are needed to integrate IBus types with a builder. Let’s start by looking at the concrete type behind the IBus interface, which is a class named BusProduct. Here is a stripped down glimpse of the source:

 

The first thing to note about this class is that it derives from BuilderProductBase, which is to be expected since we already know that it will integrate with my builder. The class also implements the IBus interface, which we’ve already looked at.

The class contains a single property, named Strategy, that contains an IBusStrategy object. If you’ve looked at any of my other packages then this should seem familiar. I tend to use strategy objects to prevent the product class from getting too complicated, which is something that can happen faster than you might believe.

The class contains an Initialize method, which it overrides from the BuilderProductBase class. At runtime, the builder will create an instance of BusProduct and will call this Initialize method to setup the object instance and get it ready for work. If we look at the initialize method in BusProduct we’ll see that it looks in a Providers collection for an IBusStrategyProvider object. The Providers property is also inherited from the BuilderProductBase class and is populated with provider objects by the builder, at runtime. Assuming we don’t find any provider objects, we simply panic and throw an exception. That’s an error scenario that should only ever happen if the caller has failed to properly configure the builder before calling it’s Build method. We (hopefully) won’t have to deal with that situation very often. On the other hand, if we do have at least one IBusStrategyProvider object in the Providers collection, we’ll pull it out and use it to create an IBusStrategy instance, using the GetStrategy method. We’ll store that strategy object in the Strategy property for use by the product at runtime.

The other method we inherited from BuilderProductBase is called Dispose. This method is called by the framework whenever the BusProduct object is disposed of. In this method we simple cleanup the strategy reference that we created in the Initialize method.

The only other two methods on BusProduct are the SendAsync and ReceiveAsynce methods. As we can see, these methods simply defer to the strategy and don’t really do much else on their own. Again, this division of labor, between the product and the strategy, allow for much greater flexibility with much lower overall complexity. For instance, using a strategy approach, we can wire up almost any kind of messaging subsystem without having to ever change any of the code in the BusProduct class.

While describing the BusProduct class I also presented several other interfaces. We should probably stop and now discuss those. The first is the IBusStrategy type. Let’s look at the simplified source code for that type now:

 

If IBusStrategy looks a lot like the IBus then that’s certainly no accident. IBusStrategy represents those parts of IBus that need to vary depending upon what kind of messaging system we are using. So, for instance, if we’re eventually using a Rabbit MQ messaging system then we’ll have the build inject a Rabbit MQ specific strategy object into BusProduct, at runtime.

Creating an IBusStrategy at runtime is the responsibility of the associated provider object. All providers for this library should implement the IBusStrategyProvider interface. Here’s a look at the simplified source for that type now:

 

So, for instance, if we eventually use a Rabbit MQ messaging system, then there will be a Rabbit MQ specific implementation of IBusStrategyProvider, that returns a Rabbit MQ specific implementation of IBusStrategy into the BusProduct.

The only missing piece at this point is the setup type. All providers have some sort of associated setup type, and for IBusStrategyProvider, the associated type is IBusStrategySetup. Here’s a look at the simplified source for that type now:

 

The setup type is intended to carry any kind of configuration information that might be required at runtime, by the strategy. It might seem odd that our setup interface is empty, but, that’s only because this is a common base for any kind of messaging related setup, and in our case, there are no setup properties that are common to all types of messaging. Later on, when we look into integrating with messaging systems like Rabbit MQ, or MSMQ, we’ll use properties on our setups that are specific to the technology. But, for now, this common interface type is fine.

I’ve mentioned several times that our IBus component integrates with a builder. Let’s look at the simplified source code for that class now:

 

If it doesn’t look like there’s much going on here it’s because, there isn’t. The BusBuilder class derives from the BuilderBase class, which is part of my builder library, and it handles almost all of the building details for us. All we really need to do is pass in the concrete product type, into the base OnBuild method, and the rest is taken care of for us.

The only piece we’re missing now is some code to inject some technology specific setup types into our builder, so it will know how to create concrete provider type(s) at runtime. For that, we’ll wait for the next article where I’ll cover the extensions I wrote for Rabbit MQ and MSMQ.

See ya then!

The code for this article is part of my NUGET package CG.Bus, which can be downloaded for free at https://github.com/CodeGator/CG.Bus

 

The source code for the CG.Bus project lives on Github and can be obtained for free at

https://www.nuget.org/packages/CG.Bus