26 November 2015

Comparing nServiceBus and MassTransit: Do we still need .Net integration frameworks?

Both nServiceBus and MassTransit address a similar set of integration problems and there are a lot of similarities between them.

They both provide a consistent messaging abstraction based on events and commands. They implement asynchronous messaging patterns such as publish\subscribe. They abstract the underlying transport away from collaborating applications and handle features such as retries and poisoned messages. They also provide support for tracking long-running transactions.

Despite this, both platforms may look increasingly out of place in a future that is likely to be dominated by more diverse technologies and autonomous agile development teams.

Why not roll your own?

MassTransit and nServiceBus were both developed nearly ten years ago to plug a gap in Microsoft’s integration landscape, i.e. there was nothing to support event-based integration. They put a framework on top of Microsoft’s default messaging transport (MSMQ) to add features such as publish\subscribe and transactional messaging (i.e. commit and retry semantics). As a commercially supported product nServiceBus also built out a range of extra features to include modelling tools and advanced monitoring facilities.

These days we have a host of broker-based messahing solutions available to us such as RabbitMQ, ActiveMQ and Azure Service Bus. They don’t come with the sophisticated tooling offered by nServiceBus, but they can provide a simple messaging fabric if you’re prepared to do some manual leg-work to connect things up.

In this context, do MassTransit nor nServiceBus offer much beyond an abstraction layer for commands and events? It’s always a good idea to separate the underlying transport to make it easier to mock or replace, but does this warrant adopting a large framework? Why not just write your own light abstraction?

The problem here is that writing your own integration is a much more involved undertaking than it might first appear. There are numerous problems you need to solve just to get a basic abstraction layer going such as deserialization, routing, transactions and retries. That’s before you’ve thought about how to handle monitoring, audit and message versioning.

It’s not hard to find examples where people have attempted to roll their own frameworks – there’s Rebus, Nimbus, and Shuttle-ESB to name just a few.  You should always think carefully before developing a custom solution to this kind of generic problem as it can leave you with quite a support burden.

The point about messaging is that it’s difficult. Many developers don’t realise just how hard it is until they are knee-deep in basic delivery issues. By this point they have built out most of nServiceBus or MassTransit, except without the same level of battle-hardened resilience, support and documentation.

Lack of cross-platform support

Both platforms are very much tied to the Microsoft ecosystem and are unable to support cross-platform integration. This effectively rules them out if there is a chance of having to integrate with an application or service created with a non-Microsoft technology.  This seems pretty inflexible unless you are prepared to use Microsoft technologies to the exclusion of all else.

Not only are the platforms tied to Microsoft, but they require very recent versions of the .Net framework – 4.5 or better. Microsoft have never offered a clear upgrade path for their development platform so there are a lot of systems out there languishing on older framework versions. You’d be surprised how many legacy systems are stuck in.Net 2.0 Web Forms or even Classic ASP.

Commercial models: choose between expensive or absent

In commercial terms, this is not a straight choice between a commercial product and open source challenger. There was a time when nServiceBus had an open source element and its licensing has evolved towards an exclusively commercial model as the platform has matured.

Current nServiceBus licensing is expensive to scale out across a large organisation – those enterprise node licenses do add up and you also have to pay for a messaging infrastructure. MassTransit may be an open source project but it has gone through some pretty quiet phases. The period between 2013 and 2014 seemed to be something of a lost year and support may remain too patchy to consider betting the farm on it across an enterprise.

Managing change

One of the more difficult challenges in service integration is managing the contracts that define service interfaces. Both nServiceBus and MassTransit define these contracts as.Net classes or interfaces which tend to make them intolerant of change.

MassTransit provides some flexibility through its use of dynamic classes but nServiceBus expects you to distribute contracts as compiled binaries between sender and consumer. From bitter personal experience I know how difficult it is to keep these up to date between development teams.

There are many ways of solving this problem. You can use a version tolerant serialization format, such as Google’s protocol buffers which was designed precisely for this problem. You can adopt a more tolerant approach to reading message payloads so you only read the parts that you are interested in rather than deserializing the whole thing.

Alas, these kinds of techniques are not available to users of nServiceBus and MassTransit. Both platforms lock you into distributing relatively strict contracts between relatively intolerant message readers.

Sagas are not the same as workflows

Both platforms also offer functionality to manage long-running processes that involve collaboration between different systems. They both refer to this as “sagas”. In both implementations, this involves running a separate service to track the status of a workflow as messages are sent between participating systems.

My reservation here is that everything involved in the saga is coupled to a workflow engine and state machine. The single most important characteristic of services is that they should be autonomous. The notion of tracking a saga using an external agent undermines this as it makes services dependent on an external agent for decision making.

A saga doesn’t have to involve an external workflow built on top of a state machine. You can take a more decoupled approach where any contextual information about a saga is passed along in the body of a message.  If a saga fails then it is up to the participants to use best efforts in handling any clean up logic.

Application-scale rather than enterprise-scale?

Recent trends such as microservices suggest that service architectures are evolving towards more diverse technologies being delivered by autonomous development teams. If you want to make a centralized integration platform work then you need a high level of control over the technologies that are used and the contracts that services provide.

Perhaps they are better suited to managing integrations between a few closely-aligned teams working with a predictable technology stack. In this sense they are effective application integration platforms rather than something that can work across a wide and diverse enterprise. The integration space has come a long way in the last ten years and more flexible strategies are needed to address the needs to cross-platform integration and distributed development.

Filed under Architecture, Integration, Messaging, SOA.