0

Greetings Stackoverflow. I've been using stackoverflow for years to find answers, and this is my first attempts to make a question myself. So feel free to tell me if I'm doing it wrong way.

Currently I'm developing a data analytical system based on microservice architecture. It is assumed that this system will consist of a dozen self-sufficient microservices communicating with each other by RabbitMQ. Each of them is encapsulated in a docker-container and the whole system is powered by docker-swarm in the production.

In particular each microservice is a node.js application and related database, connected with some ORM interface. Its task is to manage and serve data in a CRUD manner, and to provide results of some prepared queries based on the contained data. Nothing extraordinary.

To provide microservice-microservice communication I assume to use amqplib. But the way to use it is uncertain yet.

My current question is how to make use of amqplib in a OOP manner to link inter microservice communication network with application's object-related functionality? By OOP manner, I mean the possibility to replace amqplib (and RabbitMQ itself) in the long run without the need to make changes to the data-related logic.

What I really searching for is the example of currently working microservice application utilizing AMQP. I'd pretty much appreciate that if somebody could give a link to it.

And the second part of my question. Does it make sense to build microservice application based on event-driven principals, and just pass messages from RabbitMQ to the application's main event queue? So that each procedure would be called the same way, despite the fact that it is an internal or external event.

As for the abstract example of single microservice: Let's say I have an event service and a listener connected to this service:

class UserManager {
  constructor(eventService) {
    this.eventService = eventService;
    this.eventServce.on("users.user.create-request", (payload) => {
      User.create(payload); // User interface is omitted in this example 
    }
  }
}

const eventService = new EventEmmiter();
const userManager = new UserManager(eventService);

On the other hand I've got RabbitMQ connection, that is waiting for messages:

const amqp = require('amqplib');

amqp.connect('amqp-service-in-docker').then(connection => {
  connection.createChannel().then(channel => {
    // Here we use topic type of exchange to be able to filter only related messages 
    channel.assertExchange('some-exchange', 'topic'); 

    channel.assertQueue('').then(queue => {
      // And here we are waiting only the related messages 
      channel.bind(queue.queue, 'some-exchange', 'users.*');

      channel.consume(queue.queue, message => {
        // And here is the crucial part
      }
    }
  }    
}

What I'm currently think off is to just parse and forward this message to eventService and use it's routing key as the name of the event, like this:

channel.consume(query.query, message => {
  const eventName = message.fields.routingKey;
  const eventPayload = JSON.parse(message.content.toString());
  eventService.emit(eventName, eventPayload);
}

But how about RPC's? Should I make another exchange or even a channel for them with another approach, something like:

// In RPC channel
channel.consume(query.query, message => {
  eventService.once('users.user.create-response', response => {
    const recipient = message.properites.replyTo;
    const correlationId = msg.properties.correlationId;

    // Send response to specified recipient
    channel.sendToQueue(
      recipient,
      Buffer.from(JSON.stringify(resonse)),
      { 
        correlationId: correlationId
      }
    );

    channel.ack(message);
  });

  // Same thing
  const eventName = message.fields.routingKey;
  const eventPayload = JSON.parse(message.content.toString());
  eventService.emit(eventName, eventPayload);
}

And then my User class should fire 'users.user.create-response' event every time it creates a new user. Isn't this a crutch?

Psyke SPB
  • 29
  • 1
  • 5

0 Answers0