Microservices – Should They Be Independent?

Architecturemicroservices

I have the following application:

enter image description here

This application receives an API call (HTTP), does some internal work that usually includes reading or writing to a database that has 5 different tables, generates an XML file and then sends it to another system via a REST interface. This has around 5K lines of codes.

I'm now studying the microservice architecture and see how my application would/should look like if I was using it. After reading a lot of stuff, I came to two different solutions:

  1. In the first one, each microservice only serve one or a very few HTTP requests. This is good because if one microsevice is down, only a small part of my previous big app is down, but the rest is still working. However, there is a lot of code duplication between each microservice. For example, each of them has more or less the same code to generate an XML template or send request to the southbound system. I know it can be overcome by using a shared library, but then each microservice needs to use the same programming language.

    enter image description here

  2. Splitting the part of my app to smaller services. Here, I avoid code duplication and each service can use a different programming language, but if a single microservice is down, everything stops working. Moreover, this looks like a monolithic architecture, because I'm just separating the layer with HTTP request instead of a class/functions interface. It seems maybe a bit easier to understand and maintain, but not really giving anything more.

    enter image description here

I would like to have your opinion on my personal use case as I really struggle to understand how an application in a microservice world should be designed without a concrete example.

Best Answer

The objective of microservices is to provide independently deployable, loosely coupled, lean services. This means that you shall be able to change one of the microservices however you want, and can deploy it in production without changing the others.

Your scenario 1 indeed breaks the monolith down into smaller pieces. But these are not independently deployable:

However, there is a lot of code duplication between each microservice. (...) I know it can be overcome by using a shared library,

If you change some functionality in the shared code, you no longer are sure that it's interoperable with the other service. And if shared library is changed, you're no longer sure that the other service still compiles and could be patched for an emergency issue within minutes.

Your focus seems to be reliability and continuity of the microservices that you have extracted from your legacy monolith. But the common code weakens the independent continuity of services: if you have for example a vulnerability in your shared library, both services could easily be disrupted by the same hacker. Or suffer from the same nasty UB bug.

The reliability achieved with microservice is based on true independence on the functional axis. The focus is also more on scalability (see the scale cube): microservices can offer horizontal duplication (i.e. several service instances for the same API - if one breaks down, the clones continue to work), not to speak about partitioning.

For scenario 2, you could consider implementing fault tolerance by implementing horizontal scalability, i.e. having several instances of the same service running on different nodes. It's a tough change since the services have to find each other dynamically. But then, if one of the service instance is down in the chain, the other services upstream can find another working instance. A trick for achieveing this is to move from a synchronous communication to asynchronous communication via event queues or a message broker.

It's difficult based on the few elements you provide to confirm if approach 2 is the best one. Perhaps another decomposition strategy could result in even lower coupling.

I could for example think of this one, which combines both of your approaches: migrating your common XML generator features into a "Service Proxy" (the name is arbitrary: it seemed to be a relay between your new microservices and other applications), splitting the core of the monolith into highly cohesive but loosely coupled capabilities, and hiding the details of the split to the outside world behind an API gateway:

enter image description here

Related Topic