Splitting monolithic systems into microservices
Since the increasing complexity of modern software, new architecture styles are needed to compete with the high demand of the industry. The microservice architecture as proven successful in delivering scalable and maintainable software systems.
Despite the popularity of this architecture, there is an ongoing discussion on how to split monolithic systems into a set of independent services. Questions arise like: “What is the preferred size of a microservice?” or “How small is too small?”
To answer these questions, a comprehensive understanding of the microservice architecture and the advantages and disadvantages associated with it is essential.
Recap of the microservice architecture
A microservice architecture describes the approach of developing and publishing a single application using many independent services. These services work together to perform business processes. Therefore, the microservice architecture style can be considered as a manifestation of a Service-Oriented-Architecture (SOA). The style of developing a single application as a set of independent services has a number of advantages.
Advantages of a microservice architecture
This section will only present a limited subset of the most prominent advantages of the microservice architecture style.
The most popular advantage of a distributed system is efficient horizontal scalability and the associated positive impact on the overall performance of the system. Once deployed, each service can be scaled independent of the other services.
Let’s dive into an example to understand the way microservices scale. A streaming company is using a dedicated AuthorizationService, for user authentication and authorization and an VideoService for scaling and cropping videos (probably among many other microservices). During busy business days, where numerous network requests hit the AuthorizationService but no new video gets uploaded, the company can scale up the AuthorizationService independently of the VideoService to compete with the increasing number of requests. A new replica of the AuthorizationService will be created on the server infrastructure, and a load-balancer can distribute the incoming requests between all available replicas. The VideoService, on the other hand, is not replicated. The number of instances can remain at the already sufficient number. This reduces unused server resources and saves real money.
In addition to scalability, another advantage of a distributed system is, that the best fitting technology can be used for each service. When creating a monolithic system, each part of the software will usually be written in the same programming language and probably share the same database drivers and database design. In a distributed system, a performance critical part of the application might be written in a low-level language while the heavy networking part is written in a language which is good at handling a lot of Input/Output (I/O)
Moreover, developing microservices can be done in multiple independent teams. Each team is responsible for one, or for a limited number of microservices. The source code to be maintained is thus reduced and can be better and more deeply understood in its entirety, compared to the monolithic software development approach.
In conclusion, there are quite a few interesting advantages in using a microservice over a monolithic software architecture. Each service is smaller, can be developed, released and scaled independently and is easier to maintain in the future. Nevertheless, there are disadvantages to this style as well.
Disadvantages of a distributed system
The most important disadvantage of the microservice architecture style can be summarized into a single word: communication. Since each service is limited in size and independent of the entire application, multiple services must communicate with each other to execute complex business logic. This communication between the services is often very complex and done over a network. Because of the number of services (moving parts) and the network communication, distributed systems are much more error-prone than monolithic systems. It is always possible that one service cannot reach another service because of network- or system failures. In addition, in case of an error, it is much harder to identify the reason of failure.
During the development process of a distributed system, a major amount of time has to be reserved for connecting the different services with each other and fixing any communication error.
Besides that, one needs to pick the perfect fitting API-Design for the use case at hand (e.g. gRPC, REST, GraphQL or an asynchronous communication style using a message-broker (RabbitMQ, Kafka)). Typically, this decision has to be made in an early phase of the development process, where one cannot foresee whether business requirements will change. Nevertheless, the impact of the API-Design is not limited on performance, it influences key indicators of software quality like maintainability, flexibility, reliability and usability.
Due to all the above reasons, it is critical to keep communication within a network to a minimum. This can be achieved by properly separating the monolithic application into different services.
Using Domain-Driven Design to split the monolith
The principles presented by Domain-Driven Design (DDD) give valuable guidance on how to develop a microservice network. Domain-Driven Design is a concept introduced by Eric Evans in 2004 in his book “Domain-Driven Design: Tackling Complexity in Heart of Software”. It can be used to determine where to split a monolithic application into a set of independent services and ensuring the communication between the services is as sparse as possible.
Before starting with designing the architecture, the business requirements need to be split into different, mostly independent business domains. An E-Commerce system can, for example, be split into the business domains: Billing, Customer Management, Product Management and Delivery.
It can be quite hard to determine the different business domains the software is used in. Usually, each business domain has a domain expert, different government regulations and uses its own technical language. These are just some indications that we are looking at a separate business domain.
In a microservice architecture, each service should be responsible for one single business domain. The size of each service is thus determined by the size of the business domain itself. This leads to a strong cohesion of the service without leaking implementation details to other services and adhering to the Single-Responsibility principle.
A mistake that is often done is splitting the system into technical domains like image processing, email handling and similar. The reason for this is typically to avoid code repetition between different services and follow the DRY principles (don't repeat yourself). But this leads to a chatty communication between the services and the development teams. If one team for example is creating the image processing service, another one is building the customer management service of the E-Commerce shop with a profile picture upload section, and a third team is building the product handling services with countless pictures of each product, both teams need to exchange API design details and other information with the image processing team. After the services are deployed, there will be numerous requests hitting the image processing service. Any of these requests may fail.
Adhering to the DRY principles is great within a single service. You are welcome to copy code from one service and paste it into a different service.
In my opinion, the word microservice conveys a wrong approach to the development of a distributed system. The architectural style is not so much described by the size of the individual services, but by their independence. Something like "Independent-Service" would describe this architectural style more understandably. "How small is too small?" - If I had to answer this question, my answer would be: "Size does not matter" (pun intended).