The word Microservices emerged from a workshop on software architecture in May 2011, when, for the first time, architecture style was discussed. Very simply put, Microservices is like UNIX commands, which are designed to do one single thing but can be piped together to perform some complex operations. We can say that Microservices is loosely coupled, fine-grained, follows the Single Responsibility Principle [Robert C. Martin], is highly cohesive and autonomous. A more specific and clear definition by Martin Flower is, The Microservices architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and are independently deployable by fully automated deployment machinery. There is a bare minimum of centralised management for these services, which may be written in different programming languages and use different data storage technologies.
Service oriented architecture (SOA), continuous integration and deployment, domain driven development and automation have all led to the Microservices architecture. Netflix, Amazon, LinkedIn, Groupon, PayPal, Airbnb, The Guardian and many other companies have implemented or are moving towards the Microservices architecture.
To understand Microservices better, we need to understand the macro or monolithic application style first. According to Wikipedia, A monolithic application describes a single-tiered software application in which the user interface and data access code are combined into a single program from a single platform. A monolithic application is built or packaged as a single executable or deployable unit. When deployed in a production environment, it runs as a single process. In Java terms, it is a WAR or EAR file, which is deployed on a Java EE container. These types of applications have modules, components and use one or more third party libraries. All such applications have three or more layers. How the layers commonly appear in a monolithic application can be seen in Figure 1.
The three layers are:
1. Presentation layer: This consists of a client side user interface mostly consisting of HTML, CSS, etc. It is also known as the UI layer.
2. Business layer: This consists of business components, REST, etc.
3. Data layer: This normally uses JPA, Hibernate, etc.
Figure 2 illustrates the advantages of the monolithic application.
Issues related to the monolithic application
This approach works fine, but when the application becomes large and the team grows in size the drawbacks or issues start surfacing. The common issues with this type of approach are summed up below.
Difficult to deploy and maintain
If, for instance, you have an application up and running as shown in Figure 1, and you change some business components as in Figure 3, you will need to rebuild your whole application and replace the old archive ear/war (WAR is for Web Archives and EAR is Enterprise Archive in Java) with the new one. Although you havent changed anything in the other layers, you will have to redeploy the whole application. A small change needs to be rigorously tested to ensure it doesnt affect any other part of the application. So if anything changes in the monolithic application, there is no way we can test and redeploy that part onlythe whole application has to be redeployed. If the application is huge, then the size of the ear /war file is quite large and will take time in deploying. And deploying the whole application again will interrupt the background tasks, irrespective of whether they are impacted by the change or not.
Stuck with the particular technology stack or framework
The other disadvantage is that we are stuck with the technology stack or framework for the long term. If any change is to be made in the framework or technology, the whole application will be affected and may need to be rewritten. For instance, if we have selected JVM, then we cannot think beyond it. Non-JVM languages and libraries cannot be used. Updating the JVM in the stack will lead to rigorous testing to ensure the application is working. If one component wants to use the latest feature from a third party library that supports Java 8, but other components are not compatible and are still dependent on Java 6, its difficult to do so.
Scaling the whole application when only a certain part needs scaling
Scaling requires the entire application to be scaled rather than just parts of it. This requires greater resources as shown in Figure 4.
It is possible to scale horizontally by running many instances behind the load balancer. Scaling can even be done on the basis of sorting the request origin. For example, if the request comes from the Asia region, use a predefined DNS server or database server; if the request is from the European region, use the other DNS or database server, etc. The art of scalability shows scaling on three axes. When using the monolithic approach, it is not possible to scale on functional decomposition. Figure 5 shows three different axes for scaling.
Apart from this, when a new person joins a team, he or she needs to understand all the layers and has to go through thousands of lines of code so as to become a productive team member. Any new innovative idea may not be accommodated or be implemented easily, due to partitioning of the team on the basis of technology, which leads to a communication gap. Changes require a lot of approvals, which may take a long time.
The Microservices approach
The Microservices architectural style gained momentum because it allowed building the application as suites of services. Each service does exactly one thing or implements one functionality; it is independent of other services, can be deployed independently and can even use a different stack of technology one that is the most efficient for implementing a particular functionality.
Characteristics of Microservices architecture
According to Martin Flower, the common characteristics of Microservices architecture are:
Componentisation via services: Instead of building components, we can think in terms of services, which can run independently and will expose themselves as REST end points or some other mechanism.
Organised around business capabilities: We normally make teams on the basis of technology. Almost all large organisations have a UI team, a database team, an engineering team, etc hence the three-layer architecture [Figure 1]. Due to this, even a simple change needs to be communicated in a channel, and may take a long time for approval.
In the Microservices approach, the teams are cross-functional and are built according to business capabilities, such as the UI, database, project management, code, testing, etc. Each team is responsible for one service, which it builds and runs. The team is solely responsible for that service. The team size ideally follows the two pizza rule that Amazon CEO Jeff Bezos came up with the size of a team should not be larger than what two pizzas can feed.
Products not projects: In the monolithic approach, a project is built by a team, goes for testing and then moves to the maintenance phase. This is a typical project model.
In Microservices, the team is built around the service and the product it builds; the team owns it for its full life time. The Amazon concept is: You build and you run it.
Smart endpoints and dump pipes: In the SOA based approach, there is the concept of an enterprise bus which is like the brain of the system. It does everything from message routing and transformation to applying business rules, and so on.
In Microservices, on the other hand, the bus is dumb, as the services need to be decoupled and cohesive. It uses HTTP for communication, and may use REST or some other way to achieve the same results.
Decentralised governance: The monolithic application standardises on a single technology platform which may seem healthy to start with but, later, it may become a bottleneck when some of the modules or components need to use other styles.
In Microservices, each team can select a different standard for the service it is building. Netflix is a good example.
Decentralised data management: In Microservices, instead of using the same database, each service can decide its own storage. For example, LinkedIn was using Oracle when it was running as a monolithic application, but then moved to Microservices and started using the graph database for storing people and their connections, apart from Oracle. Microservices prefers that each service manages its own database.
Designed to cope with failure: When we think of service as a component, the application on the whole is to be designed to cope with the failure of the service. As this is mostly going to be a network call using an end point which is bound to fail, it should be reflected upon. Netflix uses SimianArmy to test failure.
Evolutionary design: When we think of Microservices, we think in terms of decomposing the system into small services. The key part is the notion of independent replacement and upgradeability. The Guardian website is moving towards Microservices, due to which it can easily add quick new sections for new events which can be removed quickly once the event is over, as it is a service.
To get a clearer picture, look at Figure 6, which shows a conceptual monolithic application and Figure 7, which depicts the application split into Microservices.
The Microservices approach has some drawbacks like dealing with the complexity of a distributed system. The two common points of failure are services failing to respond and high latency. Transaction management in distributed systems is difficult to maintain. It brings lot of operational overhead, and the team should be ready to manage and monitor a lot of services which are redeployed continuously. If the organisation does not have the Dev-Ops culture, moving towards Microservices is bound to fail.