From the translator: some probably already read this titanic work from Martin Fowler and his colleague James Lewis, but I nevertheless decided to translate this article. The trend of microservices is gaining momentum in the world of enterprise development, and this article is the most valuable source of knowledge, in fact, squeezing the existing experience of working with them.
The term “Microservice Architecture” has become widespread in the last few years as a description of how to design applications as a set of independently deployable services. While there is no exact description of this architectural style, there is some common set of characteristics: the organization of services around business needs, automatic deployment, transfer of logic from the message bus to receivers (endpoints), and decentralized control over languages ​​and data.Microservices is another new term on the noisy streets of software development. And although we are usually quite wary of all such new products, this particular term describes the style of software development, which we find more and more attractive. Over the past few years, we have seen many projects using this style, and the results so far have been very positive. So much so that for most of our colleagues this style becomes the main style of software development. Unfortunately, there is not so much information that describes what microservices are and how to apply them.
In short, the architectural style of microservices is an approach in which a single application is built as a set of small services, each of which works in its own process and communicates with the rest using lightweight mechanisms, usually HTTP. These services are built around business needs and deployed independently using a fully automated environment. There is an absolute minimum of centralized management of these services. By themselves, these services can be written in different languages ​​and use different data storage technologies.
In order to start a story about the style of microservices, it is best to compare it with a monolithic style: an application built as a whole. Enterprise applications often include three main parts: a user interface (usually consisting of HTML pages and javascript), a database (usually relational, with many tables) and a server. The server side processes HTTP requests, performs domain logic, requests and updates data in the database, fills in HTML pages, which are then sent to the client’s browser. Any change in the system leads to rebuilding and deployment of a new version of the server part of the application.
')
A monolithic server is a fairly obvious way to build such systems. All query processing logic is executed in a single process, while you can use the capabilities of your programming language to divide the application into classes, functions, and namespaces. You can run and test the application on the developer’s machine and use the standard deployment process to check the changes before putting them into production. You can scale monolithic applications horizontally by running multiple physical servers behind a load balancer.
Monolithic applications can be successful, but more and more people are disappointed in them, especially in light of the fact that more and more applications are deployed in the cloud. Any changes, even the smallest ones, require rebuilding and deployment of the entire monolith. Over time, it becomes more difficult to maintain a good modular structure, changes in the logic of one module tend to affect the code of other modules. It is necessary to scale the entire application, even if it is required only for one module of this application.

These inconveniences led to the architectural style of microservices: building applications as a set of services. In addition to the ability to independently deploy and scale, each service also receives a clear physical boundary that allows different services to be written in different programming languages. They can also be developed by different teams.
We do not claim that the style of microservices is an innovation. Its roots go far back in time, at least to the design principles used in Unix. But we nevertheless believe that not enough people take this style into account and that many applications will benefit if they start using this style.
Properties of microservices architecture
We cannot say that there is a formal definition of microservice style, but we can try to describe what we consider to be common characteristics of applications using this style. Not always they are found in one application all at once, but, as a rule, each such application includes most of these characteristics. We will try to describe what we see in our own development and in the development of teams known to us.
Splitting through services
During the entire period of our stay in the industry, we see a desire to build systems by connecting different components together, in much the same way as in the real world. Over the past couple of decades, we have seen a large increase in the number of libraries used in most programming languages.
Speaking of components, we face difficulties in defining what a component is. Our definition of this: a component is a piece of software that can be independently replaced or updated.
The microservice architecture uses libraries, but their main way of partitioning an application is by dividing it into services. We define libraries as components that connect to the program and are called by it in the same process, while services are components that run in a separate process and communicate with each other via web requests or remote procedure call (RPS).
The main reason for using services instead of libraries is an independent deployment. If you are developing an application consisting of several libraries working in one process, any change in these libraries leads to a redeployment of the entire application. But if your application is split into several services, then changes affecting any of them will require redeploying only the changed service. Of course, some changes will affect the interfaces, which, in turn, will require some coordination between different services, but the goal of a good microservice architecture is to minimize the need for such coordination by setting the right boundaries between microservices, as well as the mechanism for the evolution of service contracts.
Another consequence of using services as a component is a more explicit interface between them. Most programming languages ​​do not have a good mechanism for announcing the
Published Interface . Often, only documentation and discipline prevent the violation of encapsulation of components. Services avoid this by using an explicit remote call mechanism.
However, using services in this way has its drawbacks. Remote calls are slower than calls within the process, and therefore the API should be less detailed (coarser-grained), which often leads to inconvenience in use. If you need to change the set of responsibilities between components, it is more difficult to do this because you need to cross the process boundary.
In the first approximation, we can observe that services relate to processes as one to one. In fact, a service can contain many processes that will always be developed and deployed together. For example, an application process and a database process that only this application uses.
Organization around business needs
When a large application is broken up, management often focuses on technology, which leads to the formation of a UI command, server command, and command database. When teams are broken up in this way, even small changes take a lot of time because of the need for cross-team interaction. This leads to the fact that teams place any logic on those layers that they have access to. Conway's Law in action.
“Any organization that designs a system (in a broad sense) will receive a design, whose structure copies the structure of the teams in this organization”
- Melvyn Conway, 1967

Conway's Law in action
The microservice approach to partitioning implies breaking up into services in accordance with the
needs of the business . Such services include the full range of technologies required for this business need, including the user interface, data storage, and any external interactions. This leads to the formation of cross-functional teams with a full set of necessary skills: user experience, database and project management.

Service boundaries supported by team boundaries
One of the companies organized in this style is
www.comparethemarket.com . Cross-functional teams are responsible for the construction and operation of each product and each product is divided into several separate services that communicate with each other via the message bus.
Large monolithic applications can also be divided into modules around business needs, although this usually does not happen. Of course, we recommend that large teams build monolithic applications in this way. The main problem here is that such applications tend to organize around too many contexts. If a monolith encompasses many contexts, it becomes too difficult for individual team members to work with them because of their large size. In addition, adherence to modular boundaries in a monolithic application requires substantial discipline. The clearly defined borders of the components of microservices simplifies the support of these borders.
How big should microservices be?
Although the term “Microservice” has become a popular name for this architectural style, the name itself leads to an excessive focus on the size of services and disputes about what the prefix “micro” means. In our conversations with those who were engaged in software partitioning on microservices, we saw different sizes. The companies that followed the rule “Team of two pizzas” (a team that can be fed with two pizzas), ie no more than 12 people (
following the rule: following this rule, I should be alone in the team ). In other companies we saw teams in which six people supported six services.
This leads to the question of whether there is a significant difference in how many people should work on the same service. At the moment, we believe that both of these approaches to building teams (1 service for 12 people and 1 service for 1 person) fit the description of the microservice architecture, but maybe we will change our opinion in the future. (
comment perev .: since the article there are many other articles that develop this topic; the most popular now is the opinion that the service should be so large that it can completely "fit in the developer's head, regardless of the number of lines of code ) .
Products, not projects
The majority of software development companies that we see use a design model in which the goal is to develop some piece of functionality that is then considered complete. Upon completion, this part is transferred to the support team and the project team is disbanded.
Supporters of microservices eschew this model, arguing that the team must own the product throughout its lifetime. The roots of this approach go back to Amazon, the company has a rule "
you have developed, you and support ", in which the development team takes full responsibility for the software in production. This leads to the fact that developers regularly monitor how their product behaves in production, and more contact with users, because they have to take on at least some of the support responsibilities.
Thinking in terms of the product establishes a relationship with the needs of the business. A product is not just a set of features that you need to implement. These are ongoing relationships whose goal is to help users increase their business opportunities.
Of course, this can also be achieved in the case of a monolithic application, but the high granularity of the services simplifies the establishment of personal relationships between the developers of the service and its users.
Smart receivers and stupid data channels (Smart endpoints and dumb pipes)
When building communications between processes, we witnessed many times how a significant part of logic was put into the data transfer mechanisms. A good example here is the Enterprise Service Bus (ESB). ESB products often include sophisticated capabilities for transmitting, orchestrating, and transforming messages, as well as applying business rules.
The community of microservices prefers an alternative approach: smart message receivers and stupid transmission channels. Applications built using the microservice architecture tend to be as independent (decoupled) and focused (cohesive) as possible: they contain their own domain logic and act more as filters in the classic Unix sense — they receive requests, apply logic and send a response . Instead of complex protocols such as WS- * or BPEL, they use simple REST-based protocols.
The two most commonly used protocols are HTTP requests through the resource API and a lightweight message session. The best expression first gave
Ian Robinson : "Be of the web, not behind the web."
Teams practicing microservice architecture use the same principles and protocols that the world wide web is built on (and, in fact, Unix). Frequently used resources can be cached with very little effort from developers or IT administrators.
The second most frequently used communication tool is the lightweight message bus. Such an infrastructure usually does not contain domain logic — simple implementations like RabbitMQ or ZeroMQ do nothing except provide an asynchronous factory. The logic at the same time exists at the ends of the bus - in the services that send and receive messages.
In a monolithic application, the components work in the same process and communicate with each other through a method call. The biggest problem in changing monolith to microservices lies in changing the communication pattern. One-to-one naive porting leads to chatty communications that do not work too well. Instead, you should reduce the number of communications between modules.
Decentralized management
One consequence of centralized management is the trend towards standardization of the platforms used. Experience shows that such an approach restricts the choice too much - not every problem is a nail and not every solution is a hammer. We prefer to use the right tool for each particular job. And although monolithic applications can also be written using different languages ​​in some cases, this is not standard practice.
Breaking a monolith into services, we have the choice of how to build each of them. Want to use Node.js for simple report pages? You are welcome. C ++ for real-time applications? Fine. Do you want to replace the database with the one that is better suited for reading operations of your component? For God's sake.
Of course, just because you can do something doesn't mean that you have to do it. But partitioning the system in this way gives you a choice.
Teams that develop microservices also prefer a different approach to standardization. Instead of using a set of standards written by someone, they prefer the idea of ​​constructing useful tools that other developers can use to solve similar problems. These tools are usually isolated from the code of one of the projects and shared between different teams, sometimes using the model of internal open source. Now that git and github have become the de facto standard version control system, open-source practices are becoming more and more popular in companies' internal projects.
Netflix is ​​a good example of an organization that follows this philosophy. Sharing useful and, moreover, code tested on combat servers in the form of libraries encourages other developers to solve similar problems in a similar way, leaving the possibility of choosing a different approach if necessary. Shared libraries tend to focus on common issues related to data storage, interprocessor communication, and infrastructure automation.
The community of microservices appreciates service contracts, but does not like overheads and therefore uses various ways to manage these contracts. Templates such as
Tolerant Reader and
Consumer-Driven Contracts are often used in microservices, which allows them to evolve independently. Checking Consumer-Driven contracts as part of the build increases confidence in the correct functioning of the services. We know a team from Australia that uses this approach to verify contracts. This has become part of their build process: the service is assembled only up to the point that satisfies the requirements of the contract — an elegant way to circumvent the YAGNI dilemma.
Perhaps the highest point in the practice of decentralized management is the method populated by Amazon. The teams are responsible for all aspects of the software they develop, including supporting it 24/7. This devolution of responsibility level is definitely not the norm, but we are seeing more and more companies handing over responsibility to the development teams. Netflix is ​​another company practicing it. Awakening at 3 am is a very strong incentive to pay great attention to the quality of the written code.
Microservices and SOA
When we talk about microservices, the question usually arises whether this is not the usual Service Oriented Architecture (SOA), which we saw ten years ago. There is a sound grain in this matter, since The microservice style is very similar to what some SOA advocates are promoting. The problem, however, is that the
term SOA also has a lot of different meanings and, as a rule, what people call "SOA" differs significantly from the style described here, usually due to the excessive focus on the ESB used for integration monolithic applications.
In particular, we have seen so many unsuccessful implementations of SOA (starting with the tendency to hide the complexity behind ESBs, ending with failed initiatives lasting several years, which cost millions of dollars and did not do any good) that it is sometimes too difficult to ignore these problems.
Of course, many practices used in microservices come from the experience of integrating services in large organizations. The
Tolerant Reader Template is one example. Another example - the use of simple protocols - arose as a reaction to centralized standards, the complexity of which is simply
breathtaking .
These SOA problems have led some supporters of microservices to reject the term “SOA”, while others consider microservices as a form of SOA, or perhaps the correct implementation of SOA. In any case, the fact that SOA has different meanings means that it is useful to have a separate term for this architectural style.
Many languages, many possibilities
The growth of the JVM platform is one of the latest examples of language mixing within a single platform. The transition to higher-level languages ​​to gain the benefits associated with using high-level abstractions has been a common practice for decades. In the same way, as well as transition "to iron" for writing of high-performance code.
However, many monolithic applications do not require this level of performance optimization and high-level capabilities of DSL-like languages. Instead, monoliths tend to use a single language and tend to limit the number of technologies used.
Decentralized data management
Decentralized data management appears in a different form. In the most abstract sense, this means that the conceptual model of the world will differ from one system to another. This is a common problem arising from the integration of different parts of large enterprise applications: the point of view on the concept of “Client” among sales people will differ from that of the technical support team. Some attributes of the "Client" may be present in the context of salespeople and absent in the context of technical support. Moreover, attributes with the same name can have different meanings.
This problem is encountered not only in different applications, but also within a single application, especially in cases when this application is divided into separate components. This problem is well solved by the concept of
Bounded Context from Domain-Driven Design (DDD). DDD proposes dividing a complex subject area into several contexts and building relationships between them. This process is useful for both monolithic and microservice architectures, but there is a natural connection between services and contexts that helps clarify and maintain context boundaries.
In addition to the decentralization of decision-making about modeling the subject area, microservices also contribute to the decentralization of data storage methods. While monolithic applications tend to use a single database for data storage, companies often prefer to use a single database for a whole set of applications. Such solutions are usually caused by a database licensing model. Microservices prefer to give each service the opportunity to manage its own database: how to create separate instances common for a company database, and use non-standard types of databases. This approach is called
Polyglot Persistence . You can also use Polyglot Persistence in monolithic applications, but this approach is more common in microservices.

The decentralization of responsibility for data among microservices influences how this data changes. A common approach to changing data is to use transactions to ensure consistency when changing data that resides on multiple resources. This approach is often used in monolithic applications.
This use of transactions guarantees consistency, but leads to significant temporal dependence (temporal coupling), which, in turn, leads to problems when working with many services. Distributed transactions are incredibly difficult to implement and, as a result, the microservice architecture emphasizes coordination between services
without using transactions, with the explicit indication that consistency can only be eventual consistency and problems that arise are solved by compensation operations.
Managing inconsistencies in this way is a new challenge for many development teams, but this often corresponds to business practices. Often, companies seek to respond to user actions as quickly as possible and have processes that allow users to cancel actions in case of an error. The trade-off is worth it as long as the cost of correcting an error is less than the cost of losing a business using scenarios that guarantee consistency.
Standard tested in combat vs imposed standards
Teams that use microservice architecture tend to avoid strict standards set by groups of system architects. They also tend to use and even promote open standards like HTTP and ATOM.
The key difference is in how these standards are developed and implemented. Standards managed by groups like IETF become standards only when there are several implementations in successful open-source projects.
This distinguishes them from standards in the corporate world, which are often developed by groups of people with little real development experience or have too much influence from vendors.
Infrastructure Automation
Infrastructure automation techniques have evolved greatly over the past few years. The evolution of the cloud in general, and AWS in particular, has reduced the operational complexity of building, deploying and operating microservices.
Many products and systems using microservice architecture were built by teams with extensive experience in
Continuous Delivery and
Continuous Integration . Commands that build applications in a similar way intensively use infrastructure automation techniques. This is illustrated in the image below.

Since this article is not about Continuous Delivery, we will pay attention only to a couple of its key points. We want to get as much confidence that our application works, so we run a lot of automated tests.
To perform each step of automated testing, the application is deployed in a separate environment, for which automated deployment (automated deployment) is used.After you have invested time and money in automating the process of monolith deployment, the deployment of more applications (services) no longer seems so daunting. Recall that one of the goals of Continuous Delivery is to make deployment boring, so that this application alone or three does not really matter.Another area where teams use intensive infrastructure automation is microservice management in production. Unlike the deployment process, which, as described above, in monolithic applications is not very different from that in microservices, their functioning method can vary significantly.
One of the side effects of automating the deployment process is the creation of convenient tools to help developers and administrators (operations folk). Tools for managing code, deploying simple services, monitoring and logging are now fairly common. Perhaps the best example that can be found on the net is the open source toolkit from Netflix , but there are others, for example Dropwizard which we use quite intensively.Design for failure
The consequence of using services as components is the need to design applications so that they can work when individual services fail. Any access to the service may not work due to its inaccessibility. The client must respond to it as tolerantly as possible. This is a disadvantage of microservices compared to the monolith, since This adds additional complexity to the application. As a result, microservice teams constantly think about how unavailability of services should influence user experience. Netflix's Simian Army artificially causes (simulates) service failures and even data centers during the working day to test the resiliency of the application and monitoring services.This kind of automatic testing in production allows you to simulate the stress that falls on administrators and often leads to work on weekends. We do not want to say that sophisticated monitoring systems cannot be developed for monolithic applications, only that this is less common.Since services can fail at any time, it is very important to be able to quickly detect problems and, if possible, automatically restore service functionality. The microservice architecture places great emphasis on monitoring the application in real time, checking both technical elements (for example, how many requests per second the database receives) and business metrics (for example, how many orders per minute the application receives). Semantic monitoring can provide an early warning system for problem situations, allowing the development team to get involved in problem research at the earliest stages.This is especially important with the case of microservice architecture, since splitting into separate processes and communication through eventsleads to unexpected behavior. Monitoring is extremely important for identifying undesirable cases of such behavior and quickly eliminating them.Monoliths can be built as well as microservices. In fact, this is how they should be built. The difference is that knowing when services running in different processes have ceased to interact correctly with each other is much more critical. In the case of libraries located in the same process, this type of transparency is likely to be less useful.Microservice teams, as a rule, create sophisticated monitoring and logging systems for each individual service. An example would be a console showing the status (online / offline) of the service and various technical and business metrics: current throughput, request processing time, etc.Synchronous calls are considered dangerous.
Every time you have a set of synchronous calls between services, you are confronted with a downtime multiplication effect. The idle time of your system becomes the product of the idle time of the individual components of the system. You are faced with a choice: either make your calls asynchronous, or put up with downtime. For example, in www.guardian.co.uk, developers have introduced a simple rule - one synchronous call per user request. In Netflix, in general, all APIs are asynchronous.Evolutionary design
Those who practice microservice architecture usually worked a lot with evolutionary design and consider the decomposition of services as a further opportunity to give developers control over the changes (refactoring) of their applications without slowing down the development process itself. Controlling change does not necessarily mean reducing change: with the right approach and tooling, you can make frequent, quick, well-controlled changes.Every time you try to break an application into components, you are faced with the need to decide how to divide the application. Are there any principles that indicate how the best way to "cut" our application? The key property of a component is the independence of its replacement or update, which implies the existence of situations when it can be rewritten from scratch without affecting the components interacting with it. Many development teams go even further: they explicitly plan that many services will not evolve in the long run, but will simply be thrown into a landfill.The Guardian website is a good example of an application that was designed and built as a monolith, but then evolved towards microservices. The core of the site is still a monolith, but new features are added by building microservices that use the monolith API. This approach is especially useful for functionality that is inherently temporary. An example of such functionality is specialized pages for covering sports events. Such parts of the site can be quickly assembled together using fast programming languages ​​and removed as soon as the event ends. We saw a similar approach in financial systems, where new services were added under the opened market opportunities and were removed a few months or even weeks after creation.This emphasis on substitutability is a special case of a more general principle of modular design, which is that modularity is determined by the rate of change of the functional. Things that change together should be stored in one module. Parts of the system that are rarely changed should not be together with rapidly evolving services. If you regularly change two services together, think about the possibility that they should be merged.Placing the component in services adds the ability to more granular release planning. With a monolith, any changes require rebuilding and deployment of the entire application. With microservices, you need to deploy (redeploy) only those services that have changed. This allows you to simplify and speed up the release process. The disadvantage of this approach is that you have to worry that changes in one service will break the services that access it. The traditional approach to integration is to solve such problems by versioning, but microservices prefer to use versioning only when absolutely necessary . We can avoid versioning by designing services so that they are as tolerant to changes in neighboring services as possible.For microservices future?
Our main goal in writing this article was to explain the basic ideas and principles of the microservice architecture. We believe that the microservice style is an important idea worth considering for enterprise applications. Not so long ago, we developed several systems using this style and we know several other commands that use this approach.The pioneers of this architectural style known to us are such companies as Amazon, Netflix, The Guardian, the UK Government Digital Service, realestate.com.au, Forward and comparethemarket.com. The 2013 conferences were full of examples of companies moving in a direction that can be classified as microservices, for example, Travis CI. In addition, there are many organizations that have long been using what we call microservices, but do not use this name. (Often this is called SOA, although, as we have said, SOA can come in very different and often contradictory forms.)Despite all this positive experience, we do not claim that microservices are the future of software design. And although our experience is still very positive compared to the experience of using monolithic architecture, we consciously come to the fact that not enough time has passed to make such a judgment.Often the real consequences of your architectural decisions become visible only a few years after you have made them. We have seen projects in which good teams with a strong desire for modularity have developed monolithic applications that completely "rotten" after several years. Many believe that this result is less likely in the case of microservices, because boundaries between services are physical and difficult to break. However, until we see a sufficient number of time-tested systems using this approach, we cannot say for sure how mature the micro-service architecture is.There are definitely reasons why someone might consider microservice architecture not mature enough. The success of any attempts to build a component system depends on how well the components fit the application. It is difficult to understand exactly where the boundaries of the components should lie. Evolutionary design recognizes the difficulty of holding the right borders and the importance of easily changing them. When your components are services that communicate with each other remotely, refactoring is much more difficult than in the case of libraries running in the same process. Moving code between service boundaries, changing interfaces should be coordinated between different teams. You need to add layers to maintain backward compatibility. All this also complicates the testing process.Another problem is that if the components are not selected sufficiently clean, the complexity is transferred from the components to the connections between the components. It creates a false sense of simplicity of individual components, while all the complexity is in places that are harder to control.There is also a team level factor. New techniques are generally accepted by stronger teams, but techniques that are more effective for stronger teams are not necessarily those for less strong development teams. We have seen many cases in which weak teams have developed intricate, unsuccessful architectures of monolithic applications, but it will take time before we see how this will end in the case of microservice architecture. Weak teams always create weak systems, it is difficult to say whether microservices will improve this situation or worsen.One of the reasonable arguments that we have heard is that you should not start development with microservice architecture. Start with a monolith, keep it modular and break it into microservices when the monolith becomes a problem. (And yet, this advice is not ideal, because good interfaces for communication within the process are not so in the case of a interservice message.) Insummary, we write this with reasonable optimism. At this point, we have seen enough examples of the microservice style to realize that it is a worthwhile development path. This is not to say with certainty what this will lead to, but one of the features of software development is that we have to make decisions based on that, often incomplete, information that we have access to at the moment.Link to original article: Microservices