This article explains monolith application, service-oriented architecture and micro-services architecture. It will explain two types of architectural designs for micro-services – vertical slice and categorical division. It will also talk about the advantages and challenges of micro-services.
In monolithic software applications, many features were put together into single bundle running in single servers/machines. In initial development of software, it seems quite easy to have the features into single software bundle.
But as more and more new features need to be added, the application size becomes big and requires more capacity in server/machine. Also if a similar software with few changes needs to be implemented, the entire application needs to be built and maintained separately.
Service Oriented Architecture (SOA):
With the emergence of Service Oriented Architecture (SOA) a monolithic application is split into layers of many applications. Common functionalities are put into a separate software bundle running in separate servers/machine, which then can be consumed by another application using a communication protocol, such as XML, JSON etc. These are called web-services.
To serve the need of one client, it requires all these services to be implemented/deployed. As there will be many clients, it is required to have a good configuration in the application. As these common functionalities and configuration needs grow, the size of these web-services increases. To serve quickly these services usually have cache mechanism implemented. Increase in size also increases the need for more cache, which in-turn requires more in-memory space.
Micro-Services are small sized web-services which are to serve single/few features. These are the result of dividing the monolith application into small parts which can be put into separate servers/machines. The idea is to have a vertical slice of the monolithic application, i.e. one micro-service application will take full responsibility of one/few functionalities from the top (presentation layer) to bottom (data layer). Separating features into different services will make it easy for enhancement with quick time to market. If one feature needs to be modified, then there is no need to build all applications. Only that micro-service can be modified and deployed. Also, as these are separate software bundles, it will avoid regression issues of modification in one feature affecting others.
But for one feature there can be multiple domain expertise needed. With a vertical slice, many knowledge may need to be put into each micro-service. I.e. one domain expertise need to be put in multiple micro-services. In turn, it can become again a monolithic application. For example in an airline reservation systems, there needs a software which can provide availability of flight seats, lounge facility from inventory and another engine to calculate prices for these based on many rules. If we were to put finding availability and price engine for a seat into one application and same for lounge into another application, it will need to repeat few same features in both the applications and it gets complicated when the business requirement needs combo offers. I.e. with vertical slice there comes difficulty when business requirements are complex. There will be need of a lot of cross-communication between micro-services and it will be an unstructured dependency.
Thinking of categories in business which is an expertise in something from the perspective of a task to be done, there are many features which can be put into the same category. Business requirements can be divided into many small such categories which are independent of each other. Example, creating printable content is an expertise where lies knowledge of different printers and how to create a printer readable format of the content to be printed. Another example, in blog websites, contents can be of a different subject category, but for web-application task will be to save the content and display on the web-page. There can be a Micro-Service for each category.
The micro-service for a category will take care of incorporating any technology present to serve its features. This is also a distributed computing with Micro-Services. Taking the previous example of printing, in airline booking/check-in applications there is a ticket/boarding pass which needs to be printed. The data needed for this is only ticket details. The format of these printable content varies by airline and as well the printer type where it will be printing. So these can be in the same category. It is convenient to put it in a single application as the business requirement is single, but only output formats vary. This printing expertise can be put in one Micro-Service application. This application can be continuously enhanced to adapt different types of related technology.
Categories need to be such that it is easy to add or remove micro-services with any channel of business, channels such as mobile, web etc. With micro-services are based on expertise, there can be a lot of cross-communication which may increase and can become messy over time. Here we would need to be cautious about how and from where to call services.
As we split into small categories, it may become a hierarchical structure. In this case, will need aggregators as we go into the higher level. I.e. many small categories can belong to a high-level category. Here we can have an aggregator or process coordinator to combine computation result of its sub-categories. However, it is always good to have less number of aggregators to reduce layers. It is good to use a generic aggregator such as GraphQL. With these aggregators, we can reduce crosstalk, i.e. communication between micro-services.
However, a central aggregator can become the single point of failure. And we need to write code here to invoke services in required order. There is a new type of programming taking its pace – ‘Reactive Programming’. It uses event sourcing technique. There will be an event stream channel where all micro-services will consume and produce events. There is no need of separate code to invoke services in the required order. Events are pushed into the event stream by a micro-service and another micro-service will be waiting for such events and once available it will consume the event. Services will be automatically invoked in required order without the need of any code and we can as well take benefit of asynchronous behavior.
An advantage of the categorical division is, one expert will be with one application and the application owner can focus on only that. The application can be quickly enhanced to adopt different technologies to serve features of the category. The application can improve its configurability by generalizing many different requirements of the same category. It will also ease change management as configurations of one category of business is with one application. It will also enable plug and play of applications serving a category. It becomes easy to choose the best vendor for each small category. Micro-Services also helps quick time to market and reduces regression issues affecting features of other categories of business. It eases continuous delivery. It also reduces cache load on a single application (as in case of monolithic application, data need of many categories of business are in a single application). It will effectively distribute cache into different micro-services.
Challenges with micro-services are network latency when we are communicating with different servers/machines compared to monolith application where the communication between modules is in the same machine. However, having an aggregator in server-side would avoid the problem of users being on a slow network. Also, with reactive programming or event sourcing, this problem can be reduced. Another challenge with micro-services is logging and tracing. Whenever there is a failure or issue it would be difficult to trace and find which micro-service is causing the failure. It would require perfect logging. But, logging would depend on the developers of a micro-service which can vary service to service. It becomes difficult to debug. However, it is good and easy to have a standard that “each application need to log input message entering the application and output message going out of the application”. This will make it easy to identify which service has the issue. Once the faulty service is identified then we can look into the detailed log of that service. Also with having aggregators, all logs of request and response to different micro-services will be at one place which simplifies tracing. Other problem mentioned by some organizations is having services built with different languages, causes a lack of ability to rotate developers with multiple teams. However, the choice of languages needs to be based on which domain the language is good for and how many developers of the language available in organization or market which the company can hire.
The effectiveness of this approach depends on how well categories are identified. Also continuous delivery requires continuous integration. It is good to put an emphasis on test automation. It is good to have immutability. I.e. new features to be added separately without changing/removing existing.
Adapting micro-service architecture is very helpful in delivering changes continuously. Categorical division of business requirement will yield easy enhancement and plug and play with the best vendor in the market. It improves maintainability and reliability as it provides an ability to deliver fixes/enhancements quickly. However, some of the challenges mentioned above need to be considered and mitigation needs to be planned. Adapting micro-services will avoid regression issues affecting features in a different module, and by having immutability it can be ensured previous services are not affected. However, it is good to have continuous integration (generalized test automation tool) and proper logging to analyze logs to find a fail point (automated log analyzer such as Spring Sleuth and Zipkin).