Unfortunately, there is no widely agreed definition of the word microservice. The following definition would be used in the context of this course:
For eg, an e-commerce system can be divided into modules to:
- product search
Normally, all of these modules would be implemented together in one application In this case a change in one of the modules can only be brought into production by bringing a new version of the entire application with all its modules into production. However, since the modules are implemented as microservices, the ordering process cannot only be modified independently of the other modules, but may also be taken into production independently.
This speeds up deployment and reduces the amount of checks required, since only one module has to be deployed. Due to this higher degree of decoupling, a big project will transform into a variety of smaller projects. Each project is under the responsibility of an individual microservice.
In order to do this at a technological level, any microservice must be an independent operation. The best approach for decoupling microservices is to have an independent virtual machine or a Docker container for each microservice.
In that case, a deployment will replace the Docker container of an individual microservice with a new Docker container, which starts the new version and its direct requests. The other microservices will not be affected if such an approach is used.
Advantages of this microservice definition
Defining microservices as individually deployable modules has many advantages:
- It’s very, very lightweight.
- It is very general and includes all sorts of structures that are generally referred to as microservices.
- The description is based on modules and is thus a well-understood term. This helps one to follow a variety of concepts on modularisation. This description further emphasizes that microservices are part of a broader structure and cannot work on their own. Microservices must be combined with other microservices.
- Independent deployment is a function that creates multiple benefits and is thus very necessary. Thus, considering its brevity, the concept describes what the most important aspect of a microservice really is.
Deployment of a monolith application
A framework which is not made up of microservices can only be implemented in its entirety. It is also called a deployment monolith. A deployment monolith can, of course, be split into modules. The word “deployment monolith” does not state the internal configuration of the device.
Size of a microservice
The definition of microservices described above does not tell much about the size of a microservice. The word microservice, of course, implies that limited resources, in particular, are meant. In reality, however, microservices can differ tremendously in size. Such microservices keep the whole team occupied, while others only have a few hundred lines of code. Thus, the scale of the microservices is not sufficient to be part of the description.
Advantages of microservices
Microservices for scaling development
One reason for the use of microservices is the easy scalability of development. Wide teams also need to work together on challenging tasks. With the help of microservices, the projects can be divided into smaller units that can work independently of each other.
- For example, the teams responsible for an individual microservice can make most technology decisions on their own.
- When the microservices are delivered as Docker containers, each Docker container only has to offer an interface for the other containers.
- The internal structure of the containers does not matter as long as the interface is present and functions correctly. Therefore, it is irrelevant which programming language a microservice is written in. Consequently, the responsible team can make such decisions on their own. Of course, the selection of programming languages can be restricted in order to avoid increased complexity.
- However, even if the choice of the programming language in a project has been restricted, a team can still independently use an updated library with a bug fix for their microservice.
- When a new feature only requires changes in one microservice, it can not only be developed independently, but it can also be brought into production on its own. This allows the teams to work on features completely independently.
Thus, with the aid of microservices, teams will operate independently in the field of logic and technology. This minimizes the teamwork commitment required for major programs.
Replacing legacy systems with microservices
Maintaining the legacy structure is also a problem since:
- The code is often badly structured.
- The changes are not checked by tests.
- Developers might have to deal with outdated technologies.
Microservices help when working with legacy systems since the existing code does not necessarily have to be changed. Instead, new microservices can replace parts of the old system. This requires integration between the old system and the new microservices, for example, via data replication, REST, messaging, or at the level of UI. Besides, problems such as a uniform single sign-on for the old system and the new microservices have to be solved.
But then the microservices are very much like a greenfield project. No preexisting codebase has to be used. In addition, developers can employ a completely different technology stack. This immensely facilitates work compared to having to modify the legacy code itself.
Sustainable development in microservices
Microservice-based architectures promise that systems remain maintainable even in the long run.
Replaceability of microservices
An important reason for this is the replaceability of microservices. When a microservice can no longer be maintained, it can be rewritten. Compared to changing a deployment monolith, this entails less effort because the microservices are much smaller.
However, it is difficult to replace a microservice, on which numerous other microservices depend since changes might affect the other microservices. Thus, to achieve replaceability, the dependencies between microservices have to be managed appropriately.
Replaceability is a great strength of microservices. Many developers work on replacing legacy systems. However, when a new system is designed, the question of how to replace this system after it has turned into a legacy system is rarely asked. Microservices with their replaceability provide an answer.
Hence, individual microservices remain maintainable. If the code of a microservice is unmaintainable, it can just be replaced and it would not influence any of the other microservices.
Dependencies have to be managed
To achieve maintainability, the dependencies between the microservices have to be managed in the long term.
In Classical Architectures
- Classical architectures often have difficulties at this level. A developer writes new code and unintentionally introduces a new dependency between two modules, which had been forbidden in the architecture.
- Typically, the mistake goes unnoticed because attention is only paid to the code level of the system and not to the architectural level.
- Often, it is not immediately clear which module a class belongs to. So it is also unclear to which module the developer just introduced a dependency.
- In this manner, more and more dependencies are introduced over time. The originally designed architecture becomes more violated, culminating in a completely unstructured system.
In the microservices architecture
- Microservices have clear boundaries due to their interface irrespective of whether the interface is implemented as a REST interface or via messaging.
- When a developer introduces a new dependency on such an interface, they will notice this because the interface has to be called appropriately. For this reason, it is unlikely that architecture violations will occur at the level of dependencies between microservices.
- The interfaces between microservices are in a way architecture firewalls since they prevent architecture violations. The concept of architecture firewalls is also implemented by architecture management tools like Sonargraph, Structure101, or jQAssistant. Advanced module concepts can also, generate such a firewall. In the Java world, OSGi limits access and visibility between modules. Access can even be restricted to individual packages or classes.
The architecture at the level of dependencies between microservices also remains maintainable. Developers cannot unintentionally add dependencies between microservices. Therefore, microservices can ensure a high architecture quality in the long term both inside each microservice and between the microservices.
Continuous delivery is an approach where software is continuously brought into production with the help of a continuous delivery pipeline. The pipeline brings the software into production via different phases. Have a look at the following drawing:
Let’s discuss each phase shown above.
- Typically, the software compilation, unit tests, and static code analysis are performed in the commit phase.
- In the acceptance test phase, automated tests assure the correctness of the software regarding domain logic.
- Capacity tests check the performance at the expected load.
- Explorative tests serve to perform not-yet-considered tests or to examine new functionalities. In this manner, explorative tests can analyze aspects that are not yet covered by automated tests.
- In the end, the software is brought into production.
Microservices represent independently deployable modules. Therefore each microservice has its own continuous delivery pipeline.
This facilitates continuous delivery.
Microservices facilitate continuous delivery
- The continuous delivery pipeline is significantly faster because the deployment units are smaller. Consequently, deployment is faster.
- Continuous delivery pipelines contain many test stages. The software has to be deployed in each stage. Faster deployments speed up the tests and therefore the pipeline.
- The tests are also faster because they need to cover fewer functionalities. Only the features in the individual microservice have to be tested, whereas in the case of a deployment monolith, the entire functionality has to be tested due to possible regressions.
- Building up a continuous delivery pipeline is easier for microservices. Setting up an environment for a deployment monolith is complicated.
- Most of the time, powerful servers are required. In addition, third-party systems are frequently necessary for tests. A microservice requires less powerful hardware. Besides, not many third-party systems are needed in the test environments.
- However, running all microservices together in one integration test can cancel out this advantage. An environment suitable for running all microservices would require powerful hardware as well as integration with all third-party systems.
- The deployment of a microservice poses a smaller risk than the deployment of a deployment monolith. In the case of a deployment monolith, the entire system is deployed anew, and in the case of a microservice, only one module. This causes fewer problems since less of the functionality is being changed.
In summary, microservices facilitate continuous delivery. Even their support of continuous delivery can be reason enough to migrate a deployment monolith to microservices
Deployment must be automated
However, note that microservice architectures can only work when the deployment is automated! Microservices substantially increase the number of deployable units compared to a deployment monolith. This is only feasible when the deployment processes are automated.
Independent deployment means that the continuous delivery pipelines have to be completely independent. Integration tests conflict with this independence. They introduce dependencies between the continuous delivery pipelines of the individual microservices. Therefore, integration tests must be reduced to the minimum. Depending on the type of communication, there are different approaches to achieve this for synchronous and asynchronous communication.
Microservice systems are more robust.
When a memory leak exists in a microservice, only this microservice is affected and crashes. The other microservices keep running. Of course, they have to compensate for the failure of the crashed microservice; this is called resilience.
To achieve resilience, microservices can cache values and use them in case of a problem. Alternatively, there might be a fallback to a simplified algorithm.
Without resilience, the availability of a microservice system might be a problem. It is likely that a microservice will fail for any reason.
- For example, due to the distribution into several processes, many more servers are involved in the system. Each of these servers can potentially fail.
- Communication bet ween microservices occurs via the network, which can also fail. Therefore, microservices need to implement resilience to achieve robustness.
Most of the time, scaling the whole system is not required. For example, for a shop system during Christmas, the catalog might be the most critical and hardware-consuming part. By scaling the complete system, the hardware is spent on parts that don’t require more power.
Each microservice can be independently scaled. It is possible to start additional instances of a microservice and distribute the load of the microservice into the instances. This can improve the scalability of a system significantly.
So, in the previous example, just the catalog would need to be scaled up. For this to work, the microservices naturally have to fulfill certain requirements. For example, they must be stateless. Otherwise, requests of a specific client cannot be transferred to another instance, because this instance than would not have the state-specific to that client.
It can be difficult to start more instances of a deployment monolith due to the required hardware. Besides, building up an environment for a deployment monolith can be complex. This can require additional services or a complex infrastructure with databases and additional software components. In the case of a microservice, the scaling can be more fine-grained so that normally fewer additional services are necessary and the basic requirements are less complex.
Free technology choice in microservices
- Each microservice can be implemented with an individual technology. This facilitates the migration to a new technology since each microservice can be migrated individually.
- In addition, it is simpler and less risky to gain experience with new technologies since they can initially be used for only a single microservice before they are employed in several microservices.
Security in microservices
Microservices can be isolated from each other.
- For example, it is possible to introduce firewalls into the communication between microservices.
- Besides, the communication between microservices can be encrypted to guarantee that the communication really originates from another microservice and is authentic. This prevents the corruption of additional microservices if a hacker takes over one microservice.
In general: isolation
In the end, many advantages of microservices can be traced back to a stronger isolation.
Ok, to sum it up:
- Microservices can be deployed in isolation, which facilitates continuous delivery.
- They are isolated in respect to failures, which improves robustness.
- The same is true for scalability. Each microservice can be scaled independently of the other microservices.
- The employed technologies can be chosen for each microservice in isolation, which allows for free technology choice.
- The microservices are isolated in such a way that they can only communicate via the network. Therefore, communication can be safeguarded by firewalls, which increases security.
- Due to this strong isolation, the boundaries between modules cannot be violated by mistake. The architecture is rarely violated; this safeguards the architecture.
- In isolation, a microservice can be replaced with a new microservice. This enables the low-risk replacement of microservices and allows one to keep the architecture of the individual microservices clean. Thus, isolation facilitates the long-term maintainability of the software.
- Decoupling is an important feature of modules. With their isolation, microservices push it to the extremes. Modules are normally only decoupled in regard to code changes and architecture. The decoupling between microservices goes far beyond that. Thanks to decoupling, microservices are smaller. This serves many purposes:
- Makes it easier to reason about them
- The security of a microservice is easier to verify
- The performance is easier to measure
- It is easier to figure out whether they work correctly
- That makes the design and also the development easier
Which of the discussed reasons for switching to microservices is the most important depends on the individual scenario. The use of microservices in a greenfield system is the one exception.
More often, a deployment monolith is replaced by a microservice system (see chapter 4). In that case, different advantages are relevant.
- The easier scaling of development can be an important reason for the introduction of microservices in such a scenario. Often, it is impossible to work quickly enough with a large number of developers on a single deployment monolith.
- The easy migration away from the legacy deployment monolith facilitates the introduction of microservices in such a scenario.
- Continuous delivery is often an additional goal. The aim is to increase the speed and reliability with which changes can be brought into production.
The scaling of development is not the only scenario for a migration. For example, when a single Scrum team wants to implement a system with microservices, scaling development would not be a sensible reason since the organization of development is not large enough for this. However, other reasons are possible. Continuous delivery, technical reasons like robustness, independent scaling, free technology choice, or sustainable development all play a role in such a scenario.
In the end, it is important to focus on increasing the business value. Depending on the scenario, an advantage in one of the previously mentioned areas might make the company more profitable or competitive, for example, faster time to market or better reliability of the system.
Microservices involve trade-offs
Depending on the aims, a team can compromise when implementing microservices.
- For example, when robustness is the goal of introducing microservices, the microservices have to be implemented as separate Docker containers.
- Each Docker container can crash without affecting the other ones.
- If robustness does not matter, other alternatives can be considered. For example, multiple microservices can run together as Java web applications in one Java application server. In this case, they all run in one process and therefore are not isolated in respect to robustness. Still, they are independently deployable.
- A memory leak in any of the microservices will cause them all to fail.
- However, such a solution is easier to operate and therefore might be the better trade-off in the end.
Two levels of microservices: Domain and technical
The technical and organizational advantages point to two levels at which a system can be divided into microservices.
- A coarse-grained division by domain enables the teams to develop largely independently and allows them to roll out a new feature with the deployment of a single microservice if it concerns just this one domain, as it usually does. However, sometimes multiple domains are involved and more than one microservice must be deployed.
- For example, in an e-commerce system, customer registration and the order process can be examples of such coarse-grained microservices.
- For technical reasons some microservices can be further divided. These microservices can then be scaled independently of the other microservices.
- When, for example, the last step of the order process is under especially high load, this last step can be implemented in a separate microservice. The microservice belongs to the domain of the order process, but for technical reasons, it is implemented as a separate microservice. This is an example of a technical division.
The drawing above shows an example for the two levels. Based on the domains, an e-commerce application is divided into the microservices:
- Full-text search
- Category-based search
- Check out
Search is further subdivided. The full-text search is separated from the category-based search.
- Independent scaling can be one reason for this. This architecture allows the system to scale the full-text search independently of the category-based search which is advantageous when both have to deal with different levels of load.
- Another reason could be the use of different technologies. The full-text search can be implemented with a full-text search engine, which is unsuitable for a category-based search.
Typical numbers of microservices in a system
It is difficult to state a typical number of microservices per system. Based on the divisions discussed in this chapter, 10-20 coarse-grained domains are usually defined, and each of these might be subdivided into one to three microservices. However, there are also systems with far more microservices.
Challenges in microservices
Increased operations effort
- The operation of a microservice system requires more effort than running a deployment monolith.
- This is due to the fact that in a microservice system, many more deployable units exist that all have to be deployed and monitored.
- This is feasible only when the operation is largely automated and the correct functioning of the microservices is guaranteed via appropriate monitoring.
Must Be Independently Deployable
- Microservices have to be independently deployable. For example, dividing them, into Docker containers is a prerequisite for this, but it is not enough on its own.
- Changes to interfaces must be implemented in such a way that an independent deployment of individual microservices is still possible.
- For example, the microservice which implements the interface has to offer the new and the old interface. Then this microservice can be deployed without requiring that the calling microservice be deployed at the same time.
Testing must be independent
- Also, testing must be independent. When all microservices have to be tested together, one microservice can block the test stage and prevent the deployment of the other microservices making testing much harder.
- Due to the split into microservices, there are more interfaces to test, and testing has to be independent for both sides of the interface
Difficult to change multiple microservices
- Changes that affect multiple microservices are more difficult to implement than the changes that concern several modules of a deployment monolith.
- In a microservice system, such changes require several deployments. These deployments must be coordinated.
- In the case of a deployment monolith, only one deployment would be necessary.
In a microservice system, the overview of the microservices can get lost. However, experience teaches that in practice, a sound domain-based division can restrict changes to one or a few microservices. Therefore, the overview of the system is less important because the interaction between the microservices hardly influences development due to the high degree of independence.
Increased latency and failures
Microservices communicate through the network. Compared to local communication
- The latency is much higher.
- It is also more likely that communication will fail.
A microservices system cannot rely on the availability of other microservices. This makes the systems more complex.
Weighing benefit and disadvantage
The most important rule is that microservices should only be used if they represent the simplest solution in a certain scenario.
The previously mentioned benefits should outweigh disadvantages resulting from the higher level of complexity for deployment and operation. Choosing a more complex solution is rarely a good idea.
Microservices represent an extreme type of modularization. Their separate deployment is the foundation for a very strong degree of decoupling. This results in numerous advantages.
A crucial benefit is isolation at different levels.
- This not only facilitates deployment but also limits potential failures to individual microservices.
- Microservices can be individually scaled, technology decisions affect only individual microservices, and security problems can also be restricted to individual microservices.
- The isolation allows one to more easily develop a microservices system with a large team because it requires less coordination between teams. In addition, smaller deployment artifacts make continuous delivery easier.
- Moreover, replacing a legacy system is much easier with microservices because new microservices can supplement the system without the necessity of large code changes in the legacy system.
- The challenges are mostly associated with operation. Appropriate technological decisions should strengthen the intended benefits, and at the same time they should minimize disadvantages like the complexity in operation.
Some FAQ on Microservices
What is meant by Microservices?
Microservices are independently deployable modules
What are Microservices used for?
Microservices are increasingly used in the emerging world as developers work to build bigger, more dynamic systems that are best designed and operated as a mix of smaller services that work together coherently for more comprehensive, application-wide features.
What are examples of Microservices?
Amazon, Uber, Netflix, Etsy etc.
Is REST API a Microservice?
No both are different in nature. It can be SOAP or rest also, Microservices are well define SOA.