NServiceBus is an implementation of a ‘service bus’ pattern for .NET. As a developer, it helps you create decoupled applications that are easier to maintain, extend, scale and test. In this article I will show you how to use NServiceBus (5) in two web applications, combined with some Ninject and a touch of the CQRS design pattern. In the next post I will also show you some actual code. You can download the source code to play with it locally.
What is a service bus, and how does it relate to Service-Oriented Architecture (SOA)?
Few terms in software development seem to be as hype-sensitive as ‘service bus’. Instead of getting bogged down in trying to define what a service bus is, I will start by explaining what class of problems a service bus is trying to solve.
Imagine that you’re working on a large web application, say a webshop. This webshop consists of many logical services that interact with each other to provide visitors with the ability to purchase products, like:
- A service to handle online payments with credit cards;
- A content management module to manage product information;
- An invoicing service that generates PDF invoices and sends them to the visitor;
- A website for browsing the product catalogue and viewing product details;
- A back-end service that shows recent purchases and allows status updates;
Traditionally, we would probably create one big solution in Visual Studio. Maybe the services would be split into different folders (with related classes) or different projects. Some developers may even opt for a service-oriented architecture (SOA), where the services are treated as separate websites or web services that encapsulate certain ‘business activities’.
Service-Oriented architecture (SOA)
In a sense, a service-oriented architecture attempts to achieve the same thing as object-oriented programming: create reusable components (or services, in this case) that hide the underlying, service-specific logic from other components. The advantages of such an approach are obvious:
- Components can be re-used in other projects. So you can reuse your online payment service in other projects that require this functionality;
- Components can be tested in isolation. You can test online payment without touching the other components, since they have nothing to do with online payments;
- Components can be replaced with other implementations. You can replace the online payment service with a service that does not work with credit cards but with some country-specific form of online payment;
- Components are more easily understood than a huge codebase where business activities and services are not clearly distinguishable;
Because of this, a service-oriented architecture is (at least in theory) superior to more traditional approaches. The biggest challenge lies in creating the required ‘loosely coupled’ services. Your services will interact at some point, like when your payment service has to instruct the invoice service to send an invoice to the visitor after payment of a purchase. In a ‘loosely coupled’ world, both services should only be aware of each other’s interfaces and location (e.g. a web address) but have no knowledge (or dependency) of the actual implementation. This is quite an architectural and design challenge (which is outside the scope of this post).
Let’s assume that we have ‘loosely coupled’ services for the aforementioned webshop:
- https://payments.webshop.com: the payments service;
- https://invoicing.webshop.com: the invoicing service;
- https://cms.webshop.com: the CMS for managing product information;
- https://www.webshop.com: the webshop;
- https://admin.webshop.com: the back-end service;
We can push the ‘loose coupling’ a bit further, as there is still one dependency we can remove: the location of the services. Imagine that your webshop is going international. Although credit cards are frequently used for online payments, many countries have their own preferred methods (like iDeal in The Netherlands, MisterCash in Belgium, etc.). You could implement these methods in your Payment service. But why not create different payment services altogether to keep the services small, testable and reusable? But this is a bit problematic as the webshop service somewhere has to have knowledge of where the payment service is. It could be a hardcoded URL of some configurable property. Although this will work when you have only a few dependencies, it will become increasingly unmanageable if the number of services (and different configurations) increases.
This is where the ‘Service Bus’ architectural pattern comes in. It introduces a messaging layer that takes care of the messaging between services. So if your webshop service wants to initiate an online payment, the service bus makes sure that this ‘message’ arrives at the right payment service. This webshop service is no longer dependent nor aware of the payment service, it only sends a message to perform a payment. Where this message goes is the responsibility of the service bus. This removes the point-to-point connection between these services and makes them truly decoupled (or ‘service-agnostic’). There are further advantages to this approach:
- You can use the service bus for routing messages to different services, pretty much ad-hoc or even deliver the messages to multiple services;
- In theory, this approach is far more scalable as it allows the same message to be transparently handled by many service instances (e.g. in a load-balanced environment);
- You can use the service bus for monitoring messaging (for auditing and logging);
Graphically, this would make our architecture look like this:
(Enterprise) Service Bus Architecture
Obviously, reducing the spider webs of point-to-point connections from our non-Service Bus approach make this easier to maintain and understand.
So, where does NServiceBus fit in?
NServiceBus is a .NET implementation of this architectural pattern. Although there are many products on the market that claim to be service busses, few actually are. What’s cool about NServiceBus is that it’s mostly (or entirely, if you want) code-based, simple to implement and very robust. And you don’t need to install specialized (server) software. A few NuGet packages will do the job.
Furthermore, NServiceBus is asynchronous. To explain why, I like to compare NServiceBus to a post office with mailboxes. A mailbox is generally owned by one person, but it’s entirely possible that multiple people - unbeknownst to senders - read mail from a single mailbox. Like NServiceBus, a post office is asynchronous in the sense that a sender can deliver a message to a mailbox at any point in time without knowing exactly when a recipient will pick it up. Perhaps some recipients reply to the sender, but this is also asynchronous. As long as the mailboxes are there, messages can be exchanged. Even if a recipient goes on holiday for a few weeks, they will - eventually - return and read their mail.
If you take this metaphor and apply it to our webshop, it means that the connections between our services have become far more robust against disruptions. Even if our invoicing service is down at the time, the payment service can still send a message that the payment was accepted and that an invoice can be generated. Once the invoice service becomes available or responsive again, it will receive the message and perform any actions necessary. With regular point-to-point connections, you have to implement error handling, wait-retry mechanisms and logging. NServiceBus - like most service buses - does this for you. NServiceBus guarantees that your messages are delivered (in the right order), unless you configure it otherwise.
Before we dive into actual code, there is one important distinction you should be aware of. With NServiceBus you can transmit two kinds of messages; Events and Commands. Events are relevant in so-called Publish/Subscribe (or Pub/Sub) scenarios. In that case, one publisher can publish a message to an unlimited number of subscribers. In contrast, a Command can only be send to a single endpoint and are necessary when there is a single logical owner if certain business activities. A bit abstract, I know. But I will show a practical example in my code examples.
In my next post, I will show and discuss some actual code and an actual application that uses NServiceBus.