Final Report

Microservices is an architectural style that has become very popular in the past few years, partly due to the advent of the cloud and container technologies making in possible. However, is this architectural style suitable for all type of applications? Even if it is suitable for your organisation, what are the prerequisites and how do you get from monolith to a more loosely-coupled, distributed microservices architecture?

Once you make the decision to move towards a microservices architecture, there are three categories of concerns to know and plan for: design, development and operational. A study done on grey literature done by Soldani, Tamburri, & Van Den Heuvel (2018) shows the coverage of the concerns.

Picture 1
Figure 1 – Coverage of the concerns in the taxonomy for pains of microservices

A. When to migrate

Before we get to these three concerns, we need to answer: when you should migrate to a microservices style architecture? To answer that we need to know when it is okay to run a monolith application. Afterall, not all applications are created as a microservices.

According to Kazanavičius & Mažeika (2019, p. 1) monolithic architecture is fine when you have an application that is simple and lightweight (so it doesn’t require scaling), and microservices are suitable when the application is complex and evolving. Think of a game software or an OS kernel. These types of applications don’t need to be re-designed to run as a microservices.

Another type of application that may not be very suitable for microservices design is critical system applications. Such systems will have very low margin of errors (one acceptable error in 109 operational hours) and now to ensure correctness of the system, you also need to make sure the underlying host environment (being cloud or docker container running on top of Linux) is also error free. A brief analysis of Linux shows there is always about 5000 defects waiting to be fixed. (Fetzer, 2016)

Another reason to not migrate to a microservices architecture is related to scaling. Typically, microservices, due to its nature being distributed and decoupled, can be scaled horizontally (running multiple services to manage the load) whereas monoliths can only be limitedly scaled and only vertically (getting more CPU/RAM or faster disks). A comparative research done by Al-Debagy & Martinek (2018) shows that both architectures behave similarly under normal load, however microservices perform better under load and concurrency.

Kazanavičius & Mažeika also summarize that you should migrate to microservices when:

  • The monolith is too complex to maintain
  • You benefit from decentralization and modularization of the monolith
  • You see importance in the long run (as there’ll be a lot of growing-pain for the short time)

Another way to look at this comparison is based on the database and underlying domain transactional consistency requirements. Often it is the case that monoliths rely on ACID (atomicity, consistency, isolation and durability) which means the data is always updated in a consistent manner. Due to the distributed nature of microservices and the fact that services run on multiple machines (sometimes local as well as the cloud), it is not possible to achieve reliable transactionality. The distributed systems instead rely on BASE (basic availability, soft state and eventual consistency) which means that the data might be in an incomplete or inconsistent state at some point (while transactions are in flight). If the complexity you are dealing with is due to large flows of data or rapidly changing data structures, you need to consider microservices. (Singleton, 2016)

B. Organisational Concerns

An often side-effect of designing a system with microservices is that instead of having to run a monolith, which often consists of a single physical artefact, you end up having tens and sometimes hundreds of physical services to deploy, run and maintain. To add to this complexity, components that were tightly-coupled now are potentially running on a remote machine as a distributed service. This requires a different organisational skill set and resource group to manage.

The software development community has already established patterns such as Continuous Development, Continuous Delivery, Canary releases and Blue/Green deployment methods to address some of these concerns (Kargar & Hanifizade , 2018). Your organization should already have these practices in place and established, as that takes time.

C. Design Constraints

Designing of a software system is hard on its own, but to add to the complexity, now you need to find system boundaries and how your system is composed by multiple services. When migrating an existing monolith to a microservices design, you may find that reverse engineering might help understand the system better. In a study done by Di Francesco, Lago, & Malavolta (2018) techniques such as domain decomposition, identification of new services and application of domain-driven design practices were the most common activities performed when designing a microservices system.

Picture 2
Figure 2 – Activities performed when designing a new system

Another question to answer that impacts your design decisions is how to do the migrations. The benefits of gradual refactoring vs a big-bang rewrite has been well established but if your application already has bad design, or you are running on a very old platform that you can not develop on anymore (due to the lack of development tools or developers having in-depth knowledge of the said platform), a rewrite may be justified. The suggested approach, if it is a rewrite or gradual refactoring, is to start your newly identified services with the new architecture. In the survey conducted by Di Francesco, Lago, & Malavolta (2018), 19 out of 30 participants started by implementing their microservices when introducing new services or reimplementing new functionalities and only 5 were doing a big-bang rewrite.

Picture 3
 Figure 3 – How did the respondent starts implementing Microservices

One key aspect when running microservices-based design is backwards compatibility. As stated before, the microservices system might be consisted of tens or hundreds of services, where each set of services managed by a separate autonomous team. The team has the freedom to deploy updates to the services as they see fit, without going through a concerted effort of releasing all the services at once. This means at one time, different services may rely on different versions of other services for their functionalities, so if a team deploys a new version of the service that breaks the service contract, the dependant services will fail and this failure can cascade to other dependant services, ultimately causing the whole system to fail. The solution to this is backwards compatibility and versioning of the public API or service contracts. Kargar & Hanifizade (2018) advise that “the microservices must have backward compatibility, so that each version of microservices must also support the previous version inputs” and this can be enforced with the use of regression and integration testing.

D. Development concerns

Autonomous services mean that the development team should be able to decide what technology works for them. For example, if document database is a good fit for a service, they should be able to choose that. Companies such as Netflix go as far as to say the team owning the service can choose the platform and language of their choice. This is all possible because the services are decoupled and would only expose a set of APIs to the other services.

There are seven tenets of developing microservices that is a recurring theme in studies around microservices. Those are: Fine-grained service interfaces, domain-driven design practices, cloud-native design, multiple-computing paradigms such as storage paradigms (relational database vs no-sql storage), lightweight containers, decentralized continuous delivery and DevOps. (Zimmermann, 2017)

E. Operational concerns

There are three main issues when running a distributed system in production. Monitoring, Logging and Security.

A monolith usually just runs on a machine. This means that you only have to monitor one machine to ensure smooth operation of the application. The components of the system is all tightly-coupled and run within the same process so there’s no cross-process communication. Microservices is very different. There are tens or hundreds of services running that may be running on various machines. In a mixed-environment, some services might be running locally, some on the cloud as a container or a cloud-service, and some even may run as cloud-edge. These are vastly different services that all need to be monitored to make sure the system is operational. The organisation now needs to monitor hundreds to thousands of different resources and this is not an easy task due to the system being composed of heterogeneous services. Indirect monitoring of service can report on metrics such as CPU usage or memory. While it does not require active integration with the underlying service and it is less intrusive, it may not be enough. (Cinque, Della Corte, & Pecchia, 2019).

Logging is another aspect that is quite different when comparing to running a monolith. Distributed nature of microservices makes end-to-end tracing of a process very hard, as bits of the whole process or workflow now may be owned by a different service. It is recommended to use techniques such as contextual and structural logging, using unique identifiers is requests/responses and writing logs to a local storage. (Kazanavičius & Mažeika, 2019). Using a logging server that provides searching/filtering on top of the structural logging is strongly recommended as well.

Security is another major challenge with regards to microservices. The number of services, whether running locally, in the cloud, or in mixed environment, is a cause for concern as it enlarges the attack surface. To mitigate this, there are three approaches to establish trust between various microservices: using MTLS (mutual transport layer security) to achieve authentication, using Security Tokens (ST), or using fine-grained authentication. (Yarygina & Bagge, 2018)

References

Al-Debagy, O., & Martinek, P. (2018). A Comparative Review of Microservices and Monolithic Architectures. 18th IEEE International Symposium on Computational Intelligence and Informatics. IEEE Xplor.

Cinque, M., Della Corte, R., & Pecchia, A. (2019). Advancing Monitoring in Microservices Systems. International Symposium on Software Reliability Engineering Workshops (pp. 122-123). IEEE.

Di Francesco, P., Lago, P., & Malavolta , I. (2018). Migrating towards Microservice Architectures: an Industrial Survey. International Conference on Software Architecture (pp. 32,33,34). IEEE.

Fetzer, C. (2016). Building Critical Applications Using Microservices. (p. 86). IEEE Xplore.

Kargar, M., & Hanifizade, A. (2018). Automation of Regression test in Microservice Architecture. 4th International Conference on Web Research (ICWR) (p. 134). IEEE.

Kazanavičius, J., & Mažeika, D. (2019). Migrating Legacy Software to Microservices Architecture. Open Conference of Electrical, Electronic and Information Sciences. IEEE.

Singleton, A. (2016). The Economics of Microservices. Cloud Economics, p. 20.

Soldani, J., Tamburri, D. A., & Van Den Heuvel, W.-J. (2018). The pains and gains of microservices: A Systematic grey literature review. The Journal of Systems and Software.

Yarygina , T., & Bagge, A. H. (2018). Overcoming Security Challenges in Microservice Architectures. Symposium on Service-Oriented System Engineering (pp. 11-20). IEEE.

Zimmermann, O. (2017). Microservices tenets – Agile approach to service development and deployment. Comput Science Research and Development. Springer.