A few words in defense of the monolith

    We compare the features of microservice and monolithic architecture, their advantages and disadvantages. The article was prepared for Habr based on the materials of our meta Hot Backend , which was held in Samara on February 9, 2019. We consider the factors of the choice of architecture depending on the specific task.

    Even 5 years ago, no one had heard of microservices. However, their popularity is increasing from year to year, according to statistics from Google Trends.



    Monolith and microservices: examples


    If the project uses a monolithic architecture, then the developer has only one application, all components and modules of which work with a single base.



    Microservice architecture involves breaking down into modules that run as separate processes and can have separate servers. Each microservice works with its own database, and all these services can communicate with each other both synchronously (http) and asynchronously. Moreover, to optimize the architecture, it is desirable to minimize the relationship between services.

    The diagram below is simplified, it reflects, first of all, business components.



    Microservices: benefits


    At least four advantages of microservice architecture can be distinguished:

    Independent scaling and deployment

    An independent deployment is provided for each microservice, and this is convenient when updating individual modules. If the load on the module increases, the corresponding microservice can be scaled without affecting the others. This allows you to flexibly distribute the load and save resources.

    Independent development

    Each microservice (for example, a memory module) can be developed by one team to increase the speed of creating a software product.

    Stability

    Failure of one microservice does not affect the performance of other modules.

    Heterogeneity

    Each team is free to choose its own language and technology for implementing microservices, however, it is desirable that they have compatible interfaces.

    Among developers, you can hear the opinion that monolithic architecture is outdated, it is difficult to maintain and scale, it grows rapidly in a “big lump of dirt” and is practically antipattern, that is, its presence in the code is undesirable. Large companies, such as Netflix, which switched to microservice architecture in their projects, are often cited as evidence of this opinion.

    Let's see if everyone really should switch from a monolith to microservices following the example of the largest brands?

    Switching to microservices: possible difficulties


    The first problem: decomposition

    Ideally, the application should be divided into microservices so that they interact with each other as little as possible, otherwise the application will be difficult to maintain. At the same time, decomposition is difficult to implement at the beginning of development, when business problems and the subject area can still change with the advent of new requirements. Refactoring is expensive.

    If it becomes necessary to transfer part of the functions from service A to service B, then there may be difficulties: for example, services are executed in different languages, internal calls to services become network, you need to connect other libraries. We can verify the correctness of refactoring only with the help of tests.



    Problem Two: Transactions

    Another problem is that microservices do not have the concept of distributed transactions. We can guarantee the architectural integrity of a business operation only within one microservice. If the operation involves several microservices, different databases can be used there, and such a transaction will have to be abandoned. To solve this problem, there are various methods used in business when accessibility is more important than integrity. At the same time, compensating mechanisms are provided in case something goes wrong. For example, if the goods are not in stock, you need to make a refund to the buyer's account.

    If a monolith gives us architectural integrity automatically, then with microservices you need to come up with your own mechanism and use libraries with ready-made solutions. When distributing an operation between services, it is better to request data synchronously, and perform subsequent actions asynchronously. If it is impossible to access one of the services, the team will be queued as soon as it becomes available again.

    In this regard, it is necessary to revise the approach to the user interface. The user should be notified that some actions are not performed immediately, but within a certain time. When the application is processed, he receives an invitation to see the results.



    Problem Three: Reporting

    If we use a monolithic architecture with a single database, to build a complex report you can write select and pull up several data labels: sooner or later they will be displayed. However, on microservices this data can be scattered on different bases.

    For example, we need to list companies with specific metrics. With a simple list of companies, everything works. And if you need to add metrics that lie in another database? Yes, we can make an additional request and request metrics by TIN. And if this list needs to be filtered and sorted? The list of companies can be very large, and then we have to introduce an additional service with its own database - reports.



    Problem Four: High Development Complexity

    Work on distributed services is more complicated: all requests are made over the network and can be turned off, you need to provide a callback mechanism (will it make the call again? How many times?). These are the “bricks” that gradually accumulate and contribute to increasing the complexity of the project.

    Services can be developed by several different teams, and you need to document them, keep the documentation up to date, warn other teams when changing the version. These are additional labor costs.

    If each team has an independent deployment, you need to maintain at least the previous version and disable it only after all consumers of the service have switched to the new API.
    Of course, we can take all the APIs into some kind of artifact that will be publicly available. But, firstly, services can be written in different languages, and secondly, this is not recommended. For example, in one of our projects, we refused this at the request of the customer, related to security considerations. Each microservice has a separate repository, and the customer does not give access to them.

    In the development process, everything can work correctly, and then - no. It happens that in case of exceptions, the application infinitely tries to process them, and this gives a big load - the whole system "lays down". To avoid such situations, you need to configure everything, for example, to limit the number of attempts, not to return this call to the queue in the same second, etc.





    The fifth problem: the complexity of testing, tracing and debugging

    To test a problem, you need to download all the involved microservices. Debugging becomes a non-trivial task, and all the logs need to be collected somewhere in one place. In this case, you need as many logs as possible to figure out what happened. To track the problem, you need to understand the whole path that the message has traveled. Unit tests are not enough here, since errors are likely at the junction of services. When making changes, it is possible to verify the operability only after running on the stand. We can limit each microservice to a certain amount of memory (for example, 500 megabytes), but there are times of peak load when it takes up to two gigabytes. There are times when the system starts to slow down. As a result, resources can be spent on something that does not belong to the immediate tasks of the client: for example,



    Microservice or monolith: selection criteria


    When choosing between monolithic and microservice architecture, first of all, you need to proceed from the complexity of the subject area and the need for scaling.

    If the subject area is simple, and a global increase in the number of users is not expected, then microservices can be used without a doubt. In other cases, it is better to start development on a monolith and save resources if scaling is not required. If the subject area is complex, and at the initial stage the final requirements are not defined, it is also better to start with a monolith - so as not to redo the microservices several times. With the further development of the project, it is possible to distinguish its individual parts in microservices.



    A plus is the presence of boundaries at the start of the project, as this will help not to break them during the development process. It also makes sense to start with one database, but to define a scheme for each module (for example, a payment scheme). Subsequently, this will help simplify the division of modules into microservices. In this case, we observe the boundaries of the modules and can use microservices.

    Each module must have its own API, so that later it can be allocated and made the module a microservice.



    Having determined the boundaries of the modules, you can proceed to decomposition into microservices, if necessary. In about 90% of cases it will be possible to stay on the monolith, but if necessary it will be easier and cheaper to change the architecture.

    In our practice of working with monolith and microservices, we came to the following conclusions:
    • Do not switch to microservices just because they are used by Netflix, Twitter, Facebook
    • Start with two or three microservices that interact with each other, work out all the non-functional requirements for them in detail (security, fault tolerance, scalability, etc.) and only then move on to the rest of the services
    • Automate everything possible
    • Set up monitoring
    • Write autotests
    • Do not use distributed transactions (but this is not a reason to refuse the guarantee of data integrity).
    • If you want to use a microservice architecture, be prepared for the fact that development can cost you about 3 times more than on a monolith. However, both technologies have their own disadvantages and advantages; each of them has its own niche.

    Also popular now: