22

I'm designing an application using Micro-Services and I'm unsure on the best mechanism to use to collect data from multiple services.

I believe there are two options:

  • Integrate an 'inter-service' communication mechanism that allows the services to talk directly. The API Gateway would call an individual service, which then calls other services to collect data, before returning the consolidated response to the API Gateway. The API then returns the response to the caller. (This would have to be synchronous calls when the call to serviceB requires the response from serviceA. I.E Seperate Person and Address Services.)
  • Have the API Gateway call each service directly and consolidate the data within the API before returning the response.

I'm leaning towards the second option, as having the services talk to each other would introduce coupling, in which case I might as well just architect a monolithic application. However, there are a few serious drawbacks that I can think off off the top of my head with this option:

  • Having the API execute multiple calls to multiple services increases the load on the API server, especially when some of those calls are blocking.

  • This method would mean the API has to be 'aware' of what the application is trying to do (I.E Logic would have to be programmed into the API to handle calling the services in turn, and then to consolidate the data), rather then just act as a dumb 'endpoint' for the micro-services.

I'd like to know what the standard approach to this problem is and if there is another third option that I'm missing?

  • Can you provide some context? What is your app and what is it trying to do – Ewan Oct 16 '16 at 11:04
  • My guess would be somewhere in between your two options: Each micro-service communicates with other micro-services as needed to do its job. And the API gateway could be considered to be a micro-service as well, one that primarily delegates work to other services. – Bart van Ingen Schenau Oct 16 '16 at 12:29
  • I'd argue that composing microservices on the server side defeats the main purpose of having microservices to begin with. The whole idea is to make services independent, and leave the orchestration to the client. But maybe I'm being impractical – Sridhar-Sarnobat Oct 2 '17 at 20:01
16

I would generally advise against having microservices do synchronous communication with each other, the big issue is coupling, it means the services are now coupled to each other, if one of them fails the second is now fully or partially disfunctional.

I would make a clear distinction between state changing operations and read operations (CQS Command Query Separation). For state changing operations i would use some kind of messaging infrastructure and go for fire and forget. For queries you would use Synchronous request response communication and could use an http API or just go directly to your data store.

If you are using messaging then you can also look at publish subscribe for raising events between services.

Another point to consider is (transactional) data sharing (as opposed to read only views) if you expose your internal state the reader might get the wrong state of your data, or the wrong version, and also will potentially lock your data?

Last but not least, try to do everything you can to keep your services autonomous (at least at the logical level).

Hope this makes sense.

8

It depends on why you need that data. If it is for UI, than it's perfectly fine. Moreover, it's the way it should be. Chris Richardson has a nice explanation of that concept, and Sam Newman has a great article about very similar concept called Backends for Frontends.

But if you need it for some logic, chances are that you service boundaries are wrong.

There are several characteristics that the common sense tells us our services should possess. They are:

  1. Low coupling. If you make some changes in service A, you don't want them to affect service B.
  2. High cohesion. If you need to implement some feature, you want the least possible number of services to be affected.
  3. High autonomy. If some service fails, you don't want the whole system to be down.
  4. Correct granularity. You don't want your services to be too chatty, since your network is more a complex thing than you might think.
  5. Services should communicate via events. You don't want your service to be aware of each other since it reduces maintainability. Think about what happens if you need to add a new service.
  6. Decentralized data. A service shouldn't share the way information is stored. Just like a good object, it exposes behavior, not data.
  7. Service choreography over orchestration.

In order to achieve this, treat your service boundaries as business-capabilities. A formal process of identifying service boundaries looks like the following:

  1. Identify higher-level boundaries. I like to think of them as a steps that your organisation should walk through to achieve its business-goal, to obtain its business-value. You can the idea of basic steps through taking a look at Porter's Value chain.
  2. Within each service, delve deeper. Identify child self-contained units with their own responsibilities.
  3. Mind the way they communicate. Correct services communicate primarily via events. Think about your organisational structure. Communication inside them is pretty intensive, though typically a few external events are exposed.

An example of applying this approach might be of some interest.

1

I would lean toward the second approach as well by default, though maybe not in your "API gateway", but I would consider it completely reasonable to create a new micro-service whose sole purpose was to orchestrate requests to other micro-services and represent the data in a higher-level form. In a micro-services architecture, I would lean against having the "base" micro-services communicate directly with each other.

To make this a little less subjective, let's say one service depends on another if the first requires data or services from the second, directly or indirectly. In math terms, we want this relation to be a partial order and not a preorder. In diagram form, if you plotted your dependency diagram you should get a Hasse diagram and not have any (directed) cycles. (In a Hasse diagram, the edges are implicitly directed from lower to higher.) As a further guideline, you want paths from top to bottom to generally be shorter. This means you want to more directly depend on things by default. The reasons are this minimizes the number of things that can go wrong for any particular request, it minimizes overheads, and it reduces complexity. So, in the "ideal" case by this metric, the Hasse diagram would have only two levels. Of course, there are plenty of reasons why you may want to introduce intermediate services such as caching, consolidating, load balancing, failure management.

To elaborate on your second concern of having the API Gateway be "smart", a pattern that is gaining traction now with frameworks like Falcor and Relay/GraphQL is to make requests more specifications of what to do so that the "API Gateway" can generically execute those specifications without having to know what GetTimeline entails. Instead, it would get a request like "request this user information from the user service, and get these posts from the post service" or whatever.

0

I suspect your need for services to "call into" each other indicates that you are proceeding with a system that has not been well-architectured, as this need for microservices to "call into" each other is a form of coupling that rarely appears when microservices are appropriately designed.

Are you able to explain more about the problem you are trying to solve? In simple English?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.