Wednesday 27 November 2013

Open source tools we use - addendum

I realised that in my previous post I forgot to mention log4net on the list of OSS that we use at DrDoctor.  This is probably because it's like Léon - does the job quietly, professionally and you barely notice it's there.  Until you it's really important that it's there.

So yeah, just a bit of love for log4net because it got overlooked because we don't have to think about it.

Saturday 23 November 2013

Open source tools we use

At DrDoctor we're on a .NET stack (and we're Bizspark members) but that doesn't mean we don't love open source tools - what we want is the right tools - and the .NET world is full of them. We've used a few and I thought it might be useful for people to see what choices we've made.  So here goes:

1) RabbitMQ.  Correct, it's not MS, but at no point did we ever consider using MSMQ.  It's an awesome messaging infrastructure with a great .NET client.  It works and it works well.
2) EasyNetQ Fantastic light-weight, open source Bus implementation on top of RabbitMQ.  It's actively developed, well documented and we got it working in about two days.  It's at a 0.x release at the moment, so breaking changes do happen, which means we've taken a cut of the code that we're happy with (I possibly managed to push a breaking change into our build before I realised this ;)  ).  Hopefully at some point we can contribute something back.
3) Topshelf Makes working with Windows Services a pleasure.  It's used by a lot of other projects (e.g. NServiceBus) because it's great. 
4) Castle Windsor Powerful IoC with a tonne of well written documentation.
5) Magnum library We only we use one thing from this library (the State Machine) but it's at the core of our business logic. Created by the same guys who brought you Topshelf (see above).  Almost non-existent documentation, but really handy utilities.
6) Simple Data Mark Rendle's rather excellent data access library (we use it on top of SQL Server).
7) NancyFX Nice light-weight web framework (an alternative to MS's WebAPI and MVC frameworks).

Of those six seven tools I'd say that easyNetQ, Topshelf, Magnum, NancyFX & Simple Data are all about developer productivity.  Simply put, they are tools that allow us as developers to work on business features rather than infrastructure.

RabbitMQ is a fundamental architectural choice.  DrDoctor is event driven by its very nature, so a messaging infrastructure fits perfectly.  It also leads to a clean separation of concerns with components that communicate via RabbitMQ in an asynchronous fashion.  This in turn leads to a much easier development and deployment cycle: if we're working on our appointment management logic and need to deploy an update it's one component that needs to come down, while our entire application remains up.  SOA baked in from the start.

NancyFX feels more like the Python/Rails world and doesn't carry all the kludge that comes with the .NET MVC/WebAPI frameworks.  Seriously, I don't need the Entity Framework stuff.  I really, really don't.

Which really just leaves Castle.  There are loads of IoC containers out there, I just happen to feel most comfortable with Castle. 

Saturday 9 November 2013

Changing the ways queues are named in EasyNetQ

Update: In the time it took me to make the change to our code and write the blog post below, EasyNetQ got updated, thus rendering this post obsolete.  I'll soon be doing a new version that uses the latest version.

At my current company (DrDoctor) we're using EasyNetQ as a lightweight bus implementation on top of RabbitMQ. EasyNetQ has a lot of very nice features, one of which is auto-creation of exchanges and queues. It uses the fully qualified name of the message type as its basis, but we have quite a deep hierarchy of messages, which makes sense in the tree-structure of our solution, but makes looking at the queues painful as we end up with long names.

Fortunately EasyNetQ is very extensible which allows you to change those conventions (and many others). The basis for this is taken from the documention: https://github.com/mikehadlow/EasyNetQ/wiki/Replacing-EasyNetQ-Components

The 'CreateBus' method has an overload that allows you to pass in your own services. When 'CreateBus' is called, if you pass in your own service (for instance IEasyNetQLogger from the example in the documention), your service gets registered first and so the default service registration doesn't happen (take a look at DefaultServiceProvider.Register for details).

In our case we wanted to alter the way queues and exchanges are named, which is slightly more complex because there isn't a separate service for each of those, instead there is a 'Conventions' service which sets these. However, that service is itself easy to poke from the outside. Here's some code:
 IConventions conventions = new Conventions();
conventions.QueueNamingConvention = (messageType, subscriptionId) =>
{
    var queuePrefix = EasyNetQNamingConvention.GetNameFromType(messageType);
    return string.Format("{0}:{1}", queuePrefix, subscriptionId);
};

conventions.ExchangeNamingConvention = EasyNetQNamingConvention.GetNameFromType;            
RabbitHutch.CreateBus(connectionString.ConnectionString, serviceRegister => serviceRegister.Register(provider => conventions));


The method 'GetNameFromType' is a static method that returns a string, based on the type passed in.
public static string GetNameFromType(Type type)
{
 if (type.GetInterface("IMyMessage") == null)
  throw new ArgumentException("Type must implement IMyMessage");

 string category = null;

 foreach (var attr in type.GetCustomAttributes(false).OfType<MessageCategoryAttribute>())
 {
  category = attr.Category;
 }

 if (category == null)
  throw new ArgumentException("Implementation must be marked with MessageCategoryAttribute");

 return category + "_" + type.Name;
}
This suits our needs as it means we can (and indeed have to) mark up our messages with an attribute that categorises them.

There is a bit of gotcha with this, however. We have effectively replaced the use of the EasyNetQ method 'TypeNameSerializer.Serialize' for our exchanges and message queue names, which is fine, but this method is used elsewhere that could cause a problem. There is another service that is registered
.Register<SerializeType>(x => TypeNameSerializer.Serialize)
, and SerializeType is used in DefaultMessageValidationStrategy.

Again though the solution is simple, substitute in your own implementation:

RabbitHutch.CreateBus(connectionString.ConnectionString, serviceRegister =>
 {
  serviceRegister.Register(provider => conventions);
  serviceRegister.Register<SerializeType>(provider => EasyNetQNamingConvention.GetEasyNetQNameFromType);
 });
);