Microservice design benefits and challenges
Microservices design has recently attracted significant interests of software architects. To make it short, the microservice design is a software architecture where you break down mostly server-side applications to smaller services, aka microservice, which are loosely related, independently deployable, and easily tested individually. Netflix is one of the pioneers which migrated their application to microservices. There are many advantages in choosing microservice design:
- They are loosely coupled which means that we can update/deploy services independently.
- The services are self-contained, that is you can understand the microservice code without knowing the logic of the overall application.
- Separate datastore can be used for each microservices. For example, your transaction service can store its data in MySql while your notification service is just working with Redis.
- Each microservice can scale independently from the rest of the application. You can allocate different resources (in terms of memory and computing power) to each microservice and autoscale each service individually.
- Horizontal scaling of your services is much easier than a monolith.
- The development team has the luxury to code in different language/stack for each service.
- Pivoting a microservice functionality is easier
However, these advantages can act as a double-edged sword and there are few a trade-offs choosing microservice design:
- All of the microservices APIs should be backward compatible. You cannot introduce a change in one service which affects other services functionality. If the changes are backward incompatible, you may need to change your deployment strategy and deploy the new change with corresponding fixes on other services together. Another option is to follow immutable infrastructure, that is any new changes to a microservice is released as a new microservice. The old version of that service is deprecated when all other services talk to the newer version of that service.
- Because microservices are loosely coupled, additional packages should be installed such as discovery servers, discovery clients, load balancers, API gateway, common libraries, etc
- Since independent datastores are used for each microservices, most of the performance gains through query joins in databases are not available.
- Since data are stored in different locations independently, ensuring data consistency between services sometimes become extremely difficult. For example, a service might update a field but it has to make sure (e.g. by broadcasting messages to other services) that any reference to that field gets updated on other services (e.g. updating foreign keys). A Master Data Management (MDM) service should be implemented to go through each datastores, detect inconsistencies and fix them.
- Decoupling services leads to state-less servers; in contrast to a monolith, you cannot share sessions between services which leads to more API calls comparing to a monolith to obtain the same data.
- Integration testing of the whole application is very difficult.
- Extra effort should be made to make sure each service dependencies are updated and there is no security vulnerability
- Using multiple languages might act against the development team as sharing the knowledge between teams will be difficult; in addition, maintaining different stack requires additional resources
Read Segment story about going back from microservice design to a monolith