API Design – How to Filter Data Between Microservices Using API Gateway

api-designmicroservices

I have a Location Service with an endpoint that returns people near from a given user:

GET /users/near?user=7455 

[
    {
      "userId": 1214,
      "location" : [2134983, 89384]
    },
    //more users
]

On the other hand, I have a Visits Service that manages the visits between users:

[
    {
      "userVisitor" : 7155,
      "userVisited" : 8778,
      "date" : "1991/02/10"       
    }
]

Now, from the API Gateway, I need to fetch users near from a given user and that haven't been visited yet by him. Something like this (pseudocode):

user = 7758
users = LocationService.getNearUsers(user)
output = []
for (userTarget in users) {
    if VisitsService.userHasNotVisited(user, userTarget) {
        output.add(userTarget)
    }
}
return output

Is it a good approach? Or should I implement an endpoint in Visits Service that filters the non-visited-yet users from a given users list? Something like this:

GET /visits/filter/nonvisited?visitor=7758&targets=1277,3681,3586

and do something like:

user = 7758
users = LocationService.getNearUsers(user)
output = VisitsService.filterNonVisited(user, users)
return output

Which is the better solution?

Best Answer

This is really a question of how you organise your data.

Your overall query 'Get users which have not visited user x within this area' is very relational.

You can imagine the table setup, with users, location and a many to many visits table which you can query all in one go.

Select * from users 
left join (select visits where userId= Y) 
where 
user.location is near X, 
visits.id = null

or something. You would then be asking yourself if this was worth bothering with or whether a combination of get users and get visits could achieve the same effect in a reasonable time an be more generic.

However if you have a NoSQL setup like you have indicated you are a bit stuck with your a visits because you cant really do the relational where clause.

The NoSql solution requires you group the data by a single key. Putting all the visits in a single document, possibly the same user document you are using for everything else.

This means you can achieve your result in two fast queries, but have to do the filtering in code.

The downside is that if each user has many visits you have to retrieve a lot of data you don't need. Additionally your inclusion of a date field suggests you might want to do further filtering.

One way to work around this problem is to introduce a new object which collects together the data you want.

Lets say the use case is a salesman visiting a city and he wants to visit all the customers in the city once unless he's visited them within a month.

You could generate that check list once as a slow operation, save it in a single document as a CityVisit and then have the user remove items from it each time they make a visit. Then its a quick call to retrieve the list and it can be thrown away when done.

Related Topic