Moving problems around

I recently skimmed / read this article. It is written by someone who has 25 years of experience in the field and 18 of those at Microsoft and currently operates as a Software Architect. It is not always relevant, but in this case I found it to be so.

I also made a satirical piece on this a while back already. I wish I was not this prognostic.

I will start with the premise of what the articles sketches out and then the proposed solution and lastly why that is not a good move forward and how it simultaneously looks like a better solution yet is not.

The deal with microservices

The article states that microservices can be tough to get right. There is no clear definition on how big a microservice should be and before you know it, you are actually running multiple monoliths together. There is a lot of complexity in dealing with microservices in general, and indeed one of those is software architecture related boundaries.

Scalability

Another one of those issues is mentioned in the form of scalability. It could be that microservices A and B depend on C, but it seems C is taking a long time all of a sudden. That might result in C scaling up to handle more load but also both A and B as a result of that. So in effect you could have one cascading into the other for scaling.

How would this be in monoliths, well the same. One function might cause the lag, so you scale up the entire application multiple times. In a way you only moved the problem.

Latency

Next is also increased latency. One service calls another, that calls another that calls another. This means lots of added HTTP contexts, database calls etc. This is something to take into consideration.

Deployments

To release a new version can become troublesome if you happen to release a piece of code that had incompatible changes between the latest version and the previous version and spanned two microservices that are dependent on each other. What might happen is that service A will be up but the new version of B not yet and that will result in an error and so the service is stopped and killed and then new version of B is up but the older version of A is still there as healthchecks failed.

Size boundary

Where do you stop a microservice? Personally I think a microservice should be defined by at least one of the following:

One atomic job

This means the job it does is maybe comprised of several smaller steps but they all are atomic related. For example reserving an item on a order of a customer and also decreasing the current stock to reflect that one is already taken. This needs to either happen or not at all. If you spread these two steps over two different microservices, it is a lot easier for data inconsistency to happen as maybe the order reservation succeeded but for some reason the inventory stock operation did not. Or the other way around, so the item is not being reserved somewhere but somehow one is gone missing.

This lies somewhat in the previous example as well, but all things domain related should be tied together. For example all things related to authorization can be grouped into one, or everything to do with images.

One literal function

This just means it literally has one function entrypoint that might call other functions, but it is really really small. For example only ever giving back the time with the timezone you supplied as a parameter.

In essence, microservices are difficult and complex.

Enter nanoservices

So the solution proposed is to use these nanoservices. They are actually the last variant of the previous size examples. So one function that does one thing. Only getting all users, or only getting a specific user. Take your run of the mill CRUD and each of the letters will be a nanoservice essentially.

Dependencies

In order to make this work all of the individual nanoservices have to depend on the same model that is used to represent the database. That is at least one shared dependency, so will you either get a shared lib or will you make one project that has all the functions but somehow integrate a facsimile of RPC ?

Final solution

The final proposed solution how to use the nanoservices, after choosing a way to deal with the dependency problem outlined above, is to have one microservice handle all those nanoservices. In essence taking the second example (domain related functionality) and then calling those nanoservices.

This means you have to deploy a microservice, and all relevant nanoservices if there is a change to the model. Also deploy a microservice and a relevant nanoservice if you change something in the nanoservice.

This is adding even more moving parts, making it more and more complex. Not solving one of the so called shortcomings of microservices. That it is complex and suffers from scalability issues.

Hybrid solution

There exists the option to have a monolith with some satellite services hovering that are microservices so that you get best of both worlds. It is meant for having the database models in one place, all the functionality pertaining to that in one central place but for example reporting is a separate microservice, or maybe the notifications is one such service.

The nanoservices seems to mimic that. You have the monolith in the form of the microservice, and the nanoservices as the satellite services. The problem is however that you made it one abstraction layer further, meaning there is way more to take into account considering the wiring of this grandiose architecture solution.

In this scenario you could have a monolith calling a microservice calling a nanoservice. That is introducing a lot of latency, communications, network traffic and so on.

So all in all I think the proposed solution by an industry veteran is making things worse in general and not better.

#devops