9

A popular high level architecture choice in modern program is a REST-based microservices system. This has several advantages such as loose coupling, easy re-use, limited restriction on technologies that can be used, high scalability, etc.

But one of the problems I foresee in such an architecture is poor visibility into what the dependencies of an application are. For example, let's say I have an application that uses one set of REST calls on a daily basis. This application also uses a second set of REST calls, but only once a quarter. If I was to scan the logs for the past week I would see all the daily cals, but I would likely not see the quarterly calls. When it comes time to refactor, the quarterly calls are at high risk of breaking.

What patterns or tools can be used to reduce this risk and provide greater visibility into what the dependencies of a loosely coupled architecture are?

  • 1
    This is exactly why loose coupling can be bad. When there are no compile time dependencies, the only way to detect errors, and you never catch all of them, is using automated testing. The solution is some type of automated testing, which probably includes unit testing as well as integration testing. – Frank Hileman Feb 9 '17 at 20:14
  • @FrankHileman Testing obviously help, but I find it hard to believe that this is the only solution out there. Plus there are many languages that don't have compile-time checks (ie JS or Python), so even with tight coupling there you would still have issues. – David Grinberg Feb 9 '17 at 20:17
  • 1
    static type systems can catch large numbers of errors during the compile phase. The only compensation for the lack of such a system is automated testing, to my knowledge. Static error detection via automated proofs or simply compiling will always be more reliable than tests. – Frank Hileman Feb 9 '17 at 20:20
  • One possible way could be implementing the API client of each service separately and, including these clients as dependencies of the project. With the API clients would be easier too to trace which version of the service we are consuming. – Laiv Feb 9 '17 at 21:57
  • @Laiv I'm specifically curious about RESTful services, so thats not really an option since anyone can send HTTP requests more or less. – David Grinberg Feb 9 '17 at 22:01
4

What patterns or tools can be used to reduce this risk

Keeping your APIs and your business capabilities backwards compatible.

provide greater visibility into what the dependencies of a loosely coupled architecture are

Health checks.

My service is a client for your monthly api capability. But my service is your api's client any time my service is running. So my service wakes up every 10 minutes, or whatever, connects to your monthly api, and runs the protocol to make sure that the capability my service needs is still available.

So your logs will show you how often some other service is checking to see that each particular service you offer is still available, just as it shows you how often each particular service you offer is actually used.

1

There are at least two locations where you can find the dependencies:

  • Configuration. Accessing external APIs requires to know a bunch of information about each of those APIs. Access IDs, secret keys, endpoints. All this cannot be in code, because such information will change. As an example, I recently started to migrate all my microservices to SSL. This means that every service which relies on the one being migrated should be reconfigured to point to the https:// version instead of http://. I'm glad the endpoints were in the configuration instead of being hardcoded.

  • Interfaces. You don't access a service directly from your code, because the API version will change, and you may even decide to switch to a different API. Instead, you create an abstraction layer, and uses the dependency through an interface. By following a common logic when creating those interfaces, you can make your life easier later when searching for the dependencies.

When it comes time to refactor, the quarterly calls are at high risk of breaking.

This is what regression testing is for.

You can't just look at the code, change it, and trust yourself that nothing was broken. This won't work in a microservices architecture. This won't work in a monolithic application either. A compiler can catch some of the errors you'll introduce when modifying code. In some languages, such as Haskell, the compiler can be very capable and catch most of the errors; compilers for mainstream languages, however, won't do much for you. If you don't have tests, you're screwed. The presence of microservices is irrelevant.

-2

REST APIs are loosely specified so at some point it may be useful to move to gRPC, google protobufs or Thrift to define an RPC interface and then version it.

  • 2
    This might be better as a comment... but honest this doesn't explain much. – David Grinberg Feb 28 '17 at 16:19
  • Fair enough. A rest API has no specific compile time dependency on another service because the link between the two is just an HTTP rest call, something like a host and a path. With gRPC, or Protobuf or Thrift, an interface is defined which is used to generate code. The generated code is compiled, and versioned, and then your service(s) are built against those interfaces. The result being that each service is clearly dependent on one or more of your other service interfaces. Hope that clarifies my answer! – Patrick Mar 1 '17 at 16:04

protected by gnat Mar 10 '17 at 9:07

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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