Connecting a simple client/server together with Obvs - CodeProject

:

Introduction

In this article I wanted to show to anyone interested in trying out Obvs how easy it is to quickly connect their processes together and send and receive messages over their chosen transport with very little code required. Below I will create an example solution in C# with a simple client and server.

Background

Anyone doing Domain Driven Design, building reactive applications, or designing 'microservices', will probably find themselves wanting to distribute their application over two or more processes at some point. Sending and receiving messages between processes can involve a lot of boilerplate code for each new message or service.

Obvs is a lightweight library which wraps the complexities of your particular transport, and allows you to hook your services together with very little code required. It was created by me while writing an in-house application and is in production use. The code is open source on GitHub and the you can find the pacakges on NuGet https://www.nuget.org/packages?q=obvs.

Obvs Features

  • Simple RX based interfaces for doing pub/sub
  • Convention based messaging over topics/queues per service
  • Multiplexing of multiple message types over single topics/queues
  • Dynamic creation of deserializers per type
  • Asynchronous error handling on a separate channel
  • Easy to extend and customise, allowing integration with external systems
  • Fluent code based configuration
  • Supports ActiveMQ and NetMQ transports
  • Provides serialization for XML, JSON, and ProtoBuf

Prerequesites

To proceed you will need Visual Studio, internet access for NuGet.org, and an ActiveMQ broker to connect to. If you need to you can download and install it yourself, just follow these instructions http://activemq.apache.org/getting-started.html#GettingStarted-InstallationProcedureforWindows

Create the server

Open a new Visual Studio solution, create a new Command Line project called 'Server'. Your server will receive commands and send out events. It will also support request/response.

Open the Package Manager Console and type:

  • install-package Obvs.ActiveMQ
  • install-package Obvs.Serialization.Json

The required packages should now be installed and referenced in your Server project. For this example we will be using ActiveMQ as the transport and JSON as the serlialization format.

Create your first message contract

Create a new class library called 'Contracts', we will use this to store all our message types.

Now open the Package Manager Console and type:

  • install-package Obvs -project Contracts

The first type you will need is an interface to mark a message as belonging to particular service, which we will call IExampleServiceMessage and will extend Obvs.Types.IMessage.

public interface IExampleServiceMessage : IMessage {}

Next we are going to create a few commands to send to the server. Create a new folder in the Contracts project called Commands, and add a new class called ChangeName, which implements Obvs.Types.ICommand and your IExampleServiceMessage interface. Add a string property called Name.

public class ChangeName : ICommand, IExampleServiceMessage
{
    public string Name { get; set; }

    public override string ToString()
    {
        return string.Format("ChangeName = {0}", Name);
    }
}

We would like the server to respond to the command with an event, so create a folder called Events, and a new class called NameChanged which implements Obvs.Types.IEvent and your IExampleServiceMessage interface. Again, add a string property called Name.

public class NameChanged : IEvent, IExampleServiceMessage
{
    public string Name { get; set; }

    public override string ToString()
    {
        return string.Format("NameChanged = {0}", Name);
    }
}

Configure your ServiceBus

Go back to the server, and in your Program.cs class, add a static private function to create the Obvs.ServiceBus with a single endpoint. This can be fluently configured with a service name, an ActiveMQ broker to connect to and a serialization format. Creating the endpoint AsServer implies that it will receive commands and publish events. Remember to change the broker URI if you don't have one installed on your localhost.

private static IServiceBus CreateServiceBus()
{
   return ServiceBus.Configure()
                    .WithActiveMQEndpoints<IExampleServiceMessage>()
                        .Named("Obvs.ExampleService")
                        .ConnectToBroker("tcp://localhost:61616")
                        .SerializedAsJson()
                        .AsServer()
                    .Create();
}

Using this function create a ServiceBus in your Main method, subscribe to the ChangeName command and publish a NameChanged event when this is received. You can do this by using the Rx operator OfType<>. Also we'd like to print out any commands received to the console.

serviceBus.Commands
          .OfType<ChangeName>()
          .Subscribe(command => serviceBus.Publish(new NameChanged { Name = command.Name }));

serviceBusClient.Commands.Subscribe(Console.WriteLine);

To ensure your server doesn't exit before a command is received, use Console.ReadLine.

Create your client

Now we need something to send the ChangeName command, so let's create our client. Create another Command Line project called 'Client' and reference the 'Contracts' project.

Now open the Package Manager Console and type:

  • install-package Obvs.ActiveMQ -project Client
  • install-package Obvs.Serialization.Json -project Client

Your client will need a ServiceBusClient, so create one like we did for the Server but specify AsClient instead to indicate we will be sending commands to and receiving events from this service. The ServiceBusClient has a reduced set of methods on its interface which restrict clients from receiving commands or publishing events.

private static IServiceBusClient CreateServiceBus()
{
   return ServiceBus.Configure()
                    .WithActiveMQEndpoints<IExampleServiceMessage>()
                        .Named("Obvs.ExampleService")
                        .ConnectToBroker("tcp://localhost:61616")
                        .SerializedAsJson()
                        .AsClient()
                    .CreateClient();
}

Now in the Main method on your Client, create a ServiceBusClient and use it to send a command and print out the event you receive.

serviceBusClient.Events.Subscribe(Console.WriteLine);
serviceBusClient.Send(new ChangeName {Name = "Chris"});

Remember to use a Console.ReadLine to stop your program from exiting. Now start your Server and then your Client. You should see them sending and receiving commands!

Adding more message types

Now that you have this all set up, to send and receive any other message types requires no extra code. All you have to do is create the classes, decorate them with the right interfaces, and start using them in your client and server. Obvs will discover the message types in your assemblies, take care of serializing and deserializing them, multiplexing them over a sensibly named ActiveMQ topic, and generally making sure they end up at the right place.

Adding more services

Adding more services is as easy as creating a new interface to identify messages as belonging to the service, and using the fluent configuration interfacess to configure your ServiceBus with an endpoint for your service. For example:

private static IServiceBus CreateServiceBus()
{
    return ServiceBus.Configure()
            .WithActiveMQEndpoints<IExampleService1Message>()
                .Named("Obvs.ExampleService1")
                .ConnectToBroker("tcp://localhost:61616")
                .SerializedAsJson()
                .AsServer()
            .WithActiveMQEndpoints<IExampleService2Message>()
                .Named("Obvs.ExampleService2")
                .ConnectToBroker("tcp://localhost:61616")
                .SerializedAsJson()
                .AsServer()
            .Create();
}

Conclusion

I hope you have found this article useful and would consider using Obvs in your project. I have included the full source for the example application, which also includes an example of request/response. Please checkout the code for Obvs on GitHub at https://github.com/inter8ection/Obvs. All comments, contributions, and feedback very welcome!