11

Let's say there are two (or more) RESTful microservices serving JSON. Service (A) stores user information (name, login, password, etc) and service (B) stores messages to/from that user (e.g. sender_id, subject, body, rcpt_ids).

Service (A) on /profile/{user_id} may respond with:

{id: 1, name:'Bob'}

{id: 2, name:'Alice'}

{id: 3, name:'Sue'}

and so on

Service (B) responding at /user/{user_id}/messages returns a list of messages destined for that {user_id} like so:

{id: 1, subj:'Hey', body:'Lorem ipsum', sender_id: 2, rcpt_ids: [1,3]},

{id: 2, subj:'Test', body:'blah blah', sender_id: 3, rcpt_ids: [1]}


How does the client application consuming these services handle putting the message listing together such that names are shown instead of sender/rcpt ids?

Method 1: Pull the list of messages, then start pulling profile info for each id listed in sender_id and rcpt_ids? That may require 100's of requests and could take a while. Rather naive and inefficient and may not scale with complex apps???

Method 2: Pull the list of messages, extract all user ids and make bulk request for all relevant users separately... this assumes such service endpoint exists. There is still delay between getting message listing, extracting user ids, sending request for bulk user info, and then awaiting for bulk user info response.

Ideally I want to serve out a complete response set in one go (messages and user info). My research brings me to merging of responses at service layer... a.k.a. Method 3: API Gateway technique.

But how does one even implement this?

I can obtain list of messages, extract user ids, make a call behind the scenes and obtain users data, merge result sets, then serve this final result up... This works ok with 2 services behind the scenes... But what if the message listing depends on more services... What if I needed to query multiple services behind the scenes, further parse responses of these, query more services based on secondary (tertiary?) results, and then finally merge... where does this madness stop? How does this affect response times?

And I've now effectively created another "client" that combines all microservice responses into one mega-response... which is no different that Method 1 above... except at server level.

Is that how it's done in the "real world"? Any insights? Are there any open source projects that are built on such API Gateway architecture I could examine?

Swartz
  • 1,051
  • 2
  • 11
  • 23

4 Answers4

8

The solution which we used for such problem was denormalization of data and events for updating.

Basically, a microservice has a subset of data it requires from other microservices beforehand so that it doesn't have to call them at run time. This data is managed through events. Other microservices when updated, fire an event with id as a context which can be consumed by any microservice which have any interest in it. This way the data remain in sync (of course it requires some form of failure mechanism for events). This seems lots of work but helps us with any future decisions regarding consolidation of data from different microservices. Our microservice will always have all data available locally for it process any request without synchronous dependency on other services

In your case i.e. for showing names with a message, you can keep an extra property for names in Service(B). So whenever a name update in Service(A) it will fire an update event with id for the updated name. The Service(B) then gets consumes the event, fetches relevant data from Service(A) and updates its database. This way even if Service(A) is down Service(B) will function, albeit with some stale data which will eventually be consistent when Service(A) comes up and you will always have some name to be shown on UI.

https://enterprisecraftsmanship.com/2017/07/05/how-to-request-information-from-multiple-microservices/

nak
  • 846
  • 2
  • 10
  • 26
  • Yep. One desirable property of microservices that people often forget is that they should be designed to run autonomously, i.e. to be able to achieve as much of their purpose as possible (in OP's example, providing a list of messages to read) without interaction with other services. Achieving that property very often includes duplication of data. I like to call this "local caching", even though its usually kept in a DB, not a cache, because it reminds people that it's not data that should be modified unless an update event arrives from the service that owns the data. – Graham Lea Nov 25 '19 at 12:09
3

You might want to perform response aggregation strategies on your API gateway. I've written an article on how to perform this on ASP.net Core and Ocelot, but there should be a counter-part for other API gateway technologies:

https://www.pogsdotnet.com/2018/09/api-gateway-response-aggregation-with.html

Allan Chua
  • 9,305
  • 9
  • 41
  • 61
3

You need to write another service called Aggregator which will internally call both services and get the response and merge/filter them and return the desired result. This can be easily achieved in non-blocking using Mono/Flux in Spring Reactive.

enter image description here

An API Gateway often does API composition.

But this is typical engineering problem where you have microservices which is implementing databases per service pattern.

The API Composition and Command Query Responsibility Segregation (CQRS) pattern are useful ways to implement queries .

kris
  • 979
  • 11
  • 15
1

Ideally I want to serve out a complete response set in one go (messages and user info).

The problem you've described is what Facebook realized years ago in which they decided to tackle that by creating an open source specification called GraphQL.

But how does one even implement this?

It is already implemented in various popular programming languages and maybe you can give it a try in the programming language of your choice.

livingmine
  • 581
  • 5
  • 6