Raven MQ – Client API Design

time to read 4 min | 768 words

There are only two topics that remains in the Raven MQ server (replication & point to point messaging), but I decided to stop for a while and focus on the client API. My experience have shown that it is so much more important than anything else to gain acceptance for the project.

One thing that I want to make clear is that this is the high level API, which has very little to do with how this is actually implemented.

The first thing to be aware of is that Raven MQ is transactional. That is, all operations either complete successfully or fail as a single unit. That makes it very easy to work with it for a set of scenarios. It is not an accident that the API is very similar to the one that you get from Rhino Service Bus or NServiceBus, although Raven MQ client API is drastically more modest in what it is trying to do.

Getting started:

var raveMQEndpoint = new RavenMQEndpoint
{
    Url = "http://localhost:8181"
};
raveMQEndpoint.Start();

Subscribing (methods):

raveMQEndpoint.Subscribe("/streams/system/notifications", (ctx, untypedMsg) =>
{
    // do something with the msg
});

raveMQEndpoint.Subscribe<LoginAboutToExpire>("/streams/user/1234", (ctx, msg) =>
{
    // do something with the msg
});

raveMQEndpoint.Subscribe<LoginExpired>("/streams/user/1234", (ctx, msg) =>
{
    // do something with the msg
});

This allows you to handle untyped messaged, or to select specific types of messages that will be handled from the stream (ignoring messages not of this type). I’ll discuss the ctx parameter at a later stage, for now, you can ignore it. What you can’t see here is that the Subscribe methods here returns an IDisposable instance, which allows you to remove the subscription. Useful for temporary subscriptions, which is something that is pretty common for the scenarios that we see Raven MQ used for.

Subscribing (classes):

raveMQEndpoint.Subscribe("/streams/user/1234", () => new LoginExpiredConsumer());

raveMQEndpoint.Subscribe("/streams/user/1234", mefContainer);

Instead of registering a single method, you can register a factory method, or a MEF container, both of which will create a consumer class for handling the messages.

Serialization:

Raven MQ doesn’t care about the serialization format, you can it messages using whatever format you like, but the client API used JSON/BSON to store the data.

Sending messages:

Remember that I talked about the ctx parameter? The RavenMQEndpoint doesn’t offer a Send() method, that is handled by the ctx paratemer, which stands for Context, obviously. The idea is quite simple, we want to make message sending transactional, so we always use a context to send them, and only if the context completed successfully can we truly consume the message and send all the messages to the server. You can think of the Context as the Raven MQ transaction.

For sending messages outside of processing an existing message, you can use:

ravenMQEndpoint.Transaction(ctx=> ctx.Send("/queues/customers/1234", updateCustomerAddress));

This gives us a very easy way of scoping multiple messages in a single transaction without awkward APIs.

Thoughts?