A microservice architecture is easier to scale than a monolith. But the benefits are not given just like that, otherwise everyone would just saw microservices and would not know grief.
For easy scalability, you have to pay dozens of design hours. To successfully break down the concept of an application into parts, a deep dive is required – to the level where the word “design” does not mean fonts with icons or even UX. As you understand, deep-sea expeditions into the abyss of architecture do not always pay off.
There are six questions, they are asked by the system architects when they want to understand whether it is time to cut microservices or you can get by with a monolith.
How to use questions
Straighten your shoulders, take a deep breath, imagine the next feature you need to fix, and try to honestly answer the questions below.
Every “yes” is a plus in favor of microservice architecture. If you have accumulated a lot of nos, you may be putting unreasonable complexity into the system.
Uneven development
Are parts of the system evolving at different speeds and/or in different directions?
If the components of the system develop unevenly, it is usually appropriate to divide it into microservices. This will allow each component to have an independent lifecycle, potentially reducing the amount of rework in the future.
In most complex systems, some modules go untouched for years, while others seem to change every release. And the load on the modules can be different.
For example, we have a service that goes to the backend for the history of client deposits. This data is static, but increases in volume over time, so it is better to issue a history in a separate microservice. So, with related statistical requests, unnecessary information will not be pulled to the front, and we will not once again warm the planet.
No general scalability requirements
Are the load and/or throughput characteristics of the parts of the system very different? Do components have different scalability requirements?
Microservices can scale at different rates. You can check whether this is necessary even with a cursory independent review of the architecture: the requirements for scaling modules are always on the surface.
An example from fintech: the account control functionality does not experience the same load as the order processing system. Before switching to microservices, we would have to scale the entire product, focusing on the most unstable component.
This approach makes product support more expensive, since the costs always follow the worst-case scenario. But if you refactor the order processing system into a microservice, you can scale each feature up and down as needed.
But you need to remember that splitting into microservices also increases the cost of the product. With a microservice architecture, infrastructure costs increase, so you have to choose the lesser of two evils according to the situation.
Requires increased resiliency
Do you need improved protection against a particular type of failure?
Sometimes we want to protect our application from certain types of crashes. Often, the main goal of such protection is to avoid a cascading failure of services in the event of an external service failure. Here we can create a microservice to isolate this dependency from the rest of the system. You can then build appropriate failover mechanisms into that service.
Suppose there is a service that receives a register of records from an external resource. And this resource at the most crucial moment issues an empty file instead of the necessary data. If other services were not ready for such an outcome of events, they will time out to process the request. As a result, in such a request there will be a transfer of timeouts between services – a cascading failure.
Facade pattern applied
Should it be easier to interact with external dependencies?
The question is similar to the previous one, but here the focus is on external dependencies (which, by the way, have a habit of changing often). This can also apply to vendor dependency – for example, when an application needs to provide for an easy change of payment processing service.
Microservices can work as a layer of indirection that shields the system from vendor lock. Instead of invoking a dependency directly, we can put an abstraction layer we control between the main application and the dependency. Additionally, we can build this layer to be easy for our application to consume while hiding the dependency complexity.
If in the future the situation changes and you have to migrate, the changes will be limited to only the facade, without much refactoring.
Various entities are used
Does the application access different entities, and also request data from the backend (or even several)?
In this case, it is better to separate the entities into microservices.
Suppose several business entities coexist in one microservice, and the service needs to access several backends to receive data. If you do not separate them into separate services, after some time this will lead to a discrepancy in the form of data for these backends.
In the responses for this service, more and more empty fields will slowly appear, which are needed for one module, but useless for another. In this case, the division into microservices is more than appropriate.
Requires an independent lifecycle
Does one or more modules require code to be committed in the production thread?
If a module needs a completely independent life cycle, it must be a microservice: its own code repository, CI / CD pipeline, and so on.
With a smaller volume, testing a microservice is much easier. But testing is not the only reason why you want to split your architecture into microservices. In some cases, a business need may push towards a microservice architecture.
Suppose business leaders have identified a new opportunity not to be missed: speed to market is paramount. If we decided to add new features to an already existing microservice, it would take too long. And the need for regression testing does not add speed: we would not be able to move at the pace that the business requires.
But a single microservice can have its own deployment pipeline. This approach allows you to iterate faster.
Outcome
The road to microservices hell is paved with good intentions . Many teams strive too much during development to perfection, trying to make literally every small feature independent, very simple and easily scalable.
But you need to remember that microservices are a powerful tool, which must be accompanied by a manual with safety precautions. When designing the next application, we try to find a compromise between the ideal architecture in a vacuum and business requirements, which in fact are the driving factor of the entire development process. Sometimes being fast is more important than being perfect.
If you have your own methodology for choosing between microservices and a monolith, tell us about it in the comments.