Messaging Patterns with Postal.NET

Introduction

Some of you may remember Postal.NET from my previous posts (here, here and here). In a nutshell, it is a framework I built for writing decoupled applications in .NET, in a publish-subscribe way. It is available as open source in GitHub (do have a look at the README file) and available in Nuget.

I recently added request-response synchronous support. This is so that two parties, without knowing exactly who they are, can engage in a direct conversation. Other enterprise buses support this feature as well.

So, with that in mind, let’s recap all of the messaging patterns we have in Postal.NET. Remember that at least you need to grab the Postal.NET Nuget package.

Publish-Subscribe

OK, this is easy: someone subscribes to a channel-topic pair and someone else publishes a message to it, either synchronously or asynchronously:

//subscriber

var subscription = Postal.Box.Subscribe("some channel", "some topic", (env) => ProcessMessage(env));

 

//synchronous publisher

Postal.Box.Publish("some channel", "some topic", someMessage);

 

//asynchronous publisher

await Postal.Box.PublishAsync("some channel", "some topic", someMessage);

It is also possible to subscribe to all messages and equally to broadcast to all channels and topics:

//subscribe to all channels and topics

var broadcastSubscription = Postal.Box.Subscribe("*", "*", (env) => ProcessAllMessages(env));

 

//broadcast to all channels and topics

await Postal.Box.PublishAsync("*", "*", shutdownMessage);

Finally, it is possible to conditionally process a message:

var filteredSubscription = Postal.Box.Subscribe("some channel", "some topic", (env) => ProcessMessage(env), (env) => env.Data is string);

Publish-Subscribe to Multiple Channels

Another option is to subscribe to multiple channel/topic pairs at the same time:

var combinedSubscription = Postal.Box.SubscribeMultiple("some channel", "a topic, another topic", (env) => ProcessMessage(env));

Here, an event will be raised if a message is published to any of the given topics, separated by commas.

Likewise, it is also possible to publish at the same time to several channels:

await Postal.Box.PublishAsync("a channel", "a topic, another topic", someMessage);

When the subscription is cancelled, it will cancel all channel/topic pairs at the same time.

Handle a Single Event

You can subscribe to an event only once and terminate the subscription as soon as you receive it, no need (or possible) to cancel it:

Postal.Box.Once("a channel", "a topic", (env) => ProcessMessage(env));

Event Composition

Yet another option is to use composition: do something only after this and that event has occurred. You need to install first the PostalWhen.NET package.

var composedSubscription = Postal

    .Box

    .When("a channel", "a topic")

    .And("a channel", "another topic", (env) => ShouldProcess(env))

    .Subscribe(env => ProcessSaga(env));

Reactive Subscriptions

Postal.NET plugs in nicely with Reactive Extensions, including all of its goodies, like buffering. Get PostalRX.NET:

var observable = Postal.Box.Observe("a channel", "a topic");

 

//groups 5 messages

var bufferedSubscription = observable

    .Buffer(5)

    //msgs is a collection of 5 envelopes

    .Subscribe((msgs) => ProcessBufferedMessage(msgs), (ex) => ProcessException(ex), () => ProcessTermination());

Request-Response

The last pattern added was request-response. With it, you can reply synchronously to the requestor, each without knowing about the other. The code for this lives in Nuget as PostalRequestResponse.NET:

var subscription = Postal.Box.Subscribe("a channel", "a topic", (env) =>

    {

        //check if it is a request-response message

        if (env.IsRequestResponse() == true)

        {

            //extract the contents from it

            var someMessage = env.Unwrap<SomeMessage>();

            Postal.Box.Reply(env, GetResponse(someMessage));

        }

    });

 

//Gets the results from GetResponse synchronously

var response = Postal.Box.Request("a channel", "a topic", someMessage);

Conclusion

You can see that Postal.NET offers some interesting patterns which hopefully cover most use cases. If you want to know more, do check out GitHub or drop me a note!

Composition in Postal.NET

Today I pushed a new feature of Postal.NET: composition. What is this? Well, think of it as (very) lightweight sagas; a subscription is only fired when a number of messages, with certain characteristics, have been received. Let’s see some examples.

First, the most basic one:

using (Postal

    .Box

    .When("channel1", "topic1")

    .And("channel2", "topic2")

    .Subscribe(env => Console.WriteLine(env.Data)))

{

    Postal.Box.Publish("channel1", "topic1", "Will not show");

    Postal.Box.Publish("channel2", "topic2", "Hello, World!");

}

Here, we wait for two messages to arrive, on two different channel/topic pairs. Only after the second does arrive do we trigger the subscriber action.

Next, a timed composition:

using (Postal

    .Box

    .When("channel1", "topic1")

    .And("channel2", "topic2")

    .InTime(TimeSpan.FromSeconds(5))

    .Subscribe(env => Console.WriteLine(env.Data)))

{

    Postal.Box.Publish("channel1", "topic1", "Will not show");

 

    Thread.Sleep(6 * 1000);

 

    Postal.Box.Publish("channel2", "topic2", "Will not show too");

}

Now we give our composition 5 seconds, from the first condition until the last one. Because we are waiting for slightly longer than that, the subscriber action will never get triggered.

Finally, a composition with conditions:

using (Postal

    .Box

    .When("channel1", "topic1", env => env.Data is int)

    .And("channel2", "topic2")

    .Subscribe(env => Console.WriteLine(env.Data)))

{

    Postal.Box.Publish("channel1", "topic1", 1);

    Postal.Box.Publish("channel2", "topic2", "Hello, World!");

}

On each of the steps, here represented by When and And, we can supply conditions, over an Envelope. If any of these conditions is not met, then the composition fails. Of course, we can add any number of conditions.

The Subscribe method must be called when we have all the conditions in place, and, as usual, it returns an IDisposable. When we want to cancel this subscription, we just dispose of it.

You can find the code in GitHub, under the PostalWhen.NET project. I will publish a Nuget package to go along with Postal.NET soon.

Hope you find this cool, as I did! Winking smile

Changes to Postal.NET

I made a few changes to my Postal.NET library: it is now released under the LGPL license, but that’s not the most important.

As of now, subscriptions do not prevent a subscriber from being garbage collected: I am using GCHandle instead of the actual Action<Envelope> delegate. I also added a minor convention that uses a class’ namespace as the channel and its name as the topic.

The most important is… it is now available in Nuget as https://www.nuget.org/packages/Postal.NET!

Enjoy, and let me know about it! Winking smile