Microservice architecture is not a silver bullet to solve all the architecture problems in the existing world. It gives a solution to many of the problems raised, but it comes with its own challenges. Decomposing database, communication between APIs, heavy DevOps work, and a team with aligned mentality are some of the initial factors to cope with while moving towards microservices. Even with successful implementation of a microservice, here are the challenges that an organization will face.
Debugging in microservices architecture will be hard for a developer. A single request to an application can result in multiple internal hits of different microservices. Each microservice will generate its own log. To find the root cause of any wrong behavior in any particular request could be a nightmare for a developer. In a distributed logging environment, it's hard to debug any issue. Setting up proper tools/scripts to collect logs from different services in some centralized place also gives a challenge to the DevOps team.
Monolithic applications are easy to monitor. In a monolithic application, there are fewer points to monitor as it is one service(big fat war). Points to monitor would include the database service, disk spaces, CPUs usage, any third-party tools and so on. Monitoring effort in microservices architecture increases exponentially. Each service requires the same number of points to monitor as normally found in one monolithic application. Imagine the alert rate when microservices numbers keep increasing in 100s. Monitoring and self-healing tools/script such as Nagios and Monit should be in place to handle and react to alerts coming from different services.
Using a shared library among different services may not be a good idea. There could be a case where one microservice A, generates user object JSON, and other microservice B, consumes user object JSON. The suggestion here is to define the user class in one JAR and add that JAR into both the microservices. This leads to two new issues. The first one is that sharing a common library will again lead microservices to stick with the same language. A secondly, this idea will increase the coupling between services, which is against the idea of microservices. Solution could be the User
class should be written in both the microservices. This can result in the failure in the Don't Repeat Yourself (DRY) principle.
In microservices architecture, serving a single request from outside (frontend or API consumer), can result in multiple internal communications between different microservices, which can result in a network latency. This can, in turn, degrade the overall application performance. It can be solved by choosing the right type of communication between APIs. It can be synchronous like RESTful web services or asynchronous like messaging. Depending on the application requirement, both types of communication can co-exist in one place. If a direct response is required, it should be synchronous like HTTP. If there is some intermediate result that should pass to the next service or services, the best way to do so is by publishing the events to topics or queues, such as Kafka or SNS.
Microservices can be deployed in different ways. Microservices usually ship with containers such as packer, Dockers, or for Amazon Web Services (AWS), one can also use Amazon Machine Image (AMI) to ship the microservice. However, AMIs take longer to create and deploy than Dockers, and Docker is a better tool for deploying a microservice. Proper versioning rules should be in place; otherwise, the whole architecture will be in a versioning hell. Typically, xx.xx.xx
is the versioning format. The right most is for smaller bugs or patches, while the middle section increases by adding any new feature in the component. The left most number increases when you have changed something big, like the communication interface model.
We will talk in detail about communication between services and deployment in the upcoming chapters.