2

We have a bunch of microservices written in different programming languages. These all communicate with each other over RabbitMQ(AMQP) middleware. There are consumers and publishers in each microservice which listen to incomming messages and publish on a RabbitMQ exchange respectively. The current architecture is shown in the image below

enter image description here

I want to decouple the Rabbit specific consumers and publishers from the microservices and implement them in a separate shared library as follows.

enter image description here

This shared library shall implement RabbitMQ exchange bindings. The consumer in this shared library, should listen to any incomming messages and invoke the relevant method in the target language. Same for publishers, the target language should be able to invoke a method in the shared library responsible to publish message on RabbitMQ.

The problem I am facing is in making target language bindings with my shared library.

How should I share the functions of the target language that can be invoked by the shared library?

The reason for having a shared library is because, I can swap the current RabbitMQ message service with any other.

I request for some suggestions to approach this problem

EDIT

These microservices are written by a variety of teams spanning across different companies.

The reason for having a shared library

  1. Since my current services directly implement RabbitMQ bindings, the integration test cases for each of the services also have a dependency on RabbitMQ.

  2. I can easily swap RabbitMQ for any other messaging service(for example Kafka) without changing my services, as this makes the services oblivious of the underlining messaging service.

1 Answers1

5

How should I share the functions of the target language that can be invoked by the shared library?

Most languages/ecosystems have packages, and a system which manages those packages and the dependencies between them: pip for Python, npm for JavaScript, NuGet for C#, RubyGems for Ruby, etc.

You create your wrapper as a package, and you rely on it from every service which uses a given language.

enter image description here

Another approach would be to use a different technology, such as HTTP (including Server-sent events) as an intermediary between the services and RabbitMQ. This being said, you'll have the exact same problem you have now if one day, you decide to move from HTTP to something else.

I want to decouple the Rabbit specific consumers and publishers from the microservices

The services are already decoupled from RabbitMQ through the use of AMQP. Essentially, you can replace RabbitMQ at any moment by any other AMQP-enabled server (note that AMQP 0.9.1 and AMQP 1.0 are two very different beasts, however). There are risks that any abstraction you'll add above AMQP would be a leaky abstraction: that is, it will look like you could replace at any moment, painlessly, your message queue service by something else, such as Redis pub-sub, but this is only in appearance. In fact, such replacement would involve major changes in most services anyway, if they were designed with RabbitMQ model in mind.

This is very similar to what happens when somebody implements a data access layer in order to isolate the application from the underlying database. The application is still designed with the underlying database in mind, it is optimized for the particular database, it uses its specific features that other databases may be missing, or works around its weak points.

This means that once the business decides to replace Oracle by PostgreSQL, it can be done with just minor changes, but when the decision is to replace Oracle by MongoDB, you're going to spend the next six months changing a lot of code way beyond the original data access layer.