Search code examples
node.jsarchitecturemicroservicescqrsevent-sourcing

Microservices Create Entity Implementation


This is a follow-up question to my issue outlined here.

The Gateway serves as an entry point to the application, to which every request from the client is made. The gateway then allocates the request to the responsible microservices and also handles authentication.

In this case the gateway listens for HTTP POST /bok and notifies the Microservice A to create a book. Thus Microservice A ist responsible for managing and storing everything about the book entity.


The following pseudo-code is a simplified implementation of this architecture:

Queue Communication queue communication

Gateway

router.post('/book', (req, res) => {
  queue.publish('CreateBook', req.body);
  queue.consume('BookCreated', (book) => {
    const user = getUserFromOtherMicroService(book.userId);
    res.json({ book, user });
  });
});

Microservcie A

queue.consume('CreateBook', (payload) => {
  const book = createBook(payload);
  eventStore.insert('BookCreated', book);
  const createdBook = updateProjectionDatabase(book);
  queue.publish('BookCreated', createdBook);
})

But I am not quite sure about this because of the following reasons:

  1. The listener for consuming BookCreated in the Gateway will be recreated every time a user requests to create a new book
  2. What if 2 users simultaneously create a book and the wrong book will be returned?
  3. I don't know how to fetch additional data (e.g. getUserFromOtherMicroService)

That's why I though about implementing this architecture:

Direct and Queue Communication direct communication

Gateway

router.post('/book', async (req, res) => {
  const book = await makeHttpRequest('microservice-a/create-book', req.body);
  const user = await makeHttpRequest('microservice-b/getUser', book.userId);
  res.json({ book, user });
});

Microservice A

router.post('/create-book', (req, res) => {
  const book = createBook(req.body);
  eventStore.insert('BookCreated', book);
  const createdBook = updateProjectionDatabase(book);
  queue.publish('BookCreated', createdBook);
  res.json(createdBook);
})

But I am also not really sure about this implementation because:

  1. Don't I violate CQRS when I return the book after creation? (because I should only return OK or ERROR)
  2. Isn't it inefficient to make another HTTP request in a microservices system?

Solution

  • Based on the comments above .

    Approach 1

    In this case your api gateway will be used to drop the message in the queue. This approach is more appropriate if your process is going to take long time and you have a queue workers sitting behind to pick up the messages and process. But your client side has to poll to get the results. Say you are looking for airline ticket . You drop the message. Your get an ID to poll. Your client will keep on polling until the results are available.

    But in this case you will have a challenge , as you drop the message how you are going to generate the ID that client would poll ? Do you assign the ID to message at Gateway and drop in the queue and return the same ID for the client to poll to get result ? again this approach is good for web/worker kind of scenario.

    Approach 2

    Since Your API gateway is custom application that would handle the authentication and redirect the request to respective service. Your Microsvc A would create book and publish the event and your Microservice B and C would be using it . Your Gateway will wait for the Microservice A to return response with ID (or event metadata of newly created object) of book that is created so you don't poll for it later and client has it. If you want you can have additional information from other microservices you can fetch at this time and can send aggregated response.

    For any data that is available in Microservice A,B,C you will be getting via Gateway. Make sure your gateway is highly available.

    Hope that helps . Let me know if you have any questions !