The microservice architecture (MSA) is becoming increasing popular when it comes to modern day software development architecture, thanks to the modularity and granularity it provides on top of other advantages like independent scalability and releasing applications in a continuous manner. However, the MSA comes with a few more issues itself. Issues of which should be carefully evaluated. Before moving your entire system to a service based architecture, lot of questions need to be asked and answered adequately. This article is to point out these potential issues and suggest possible solutions to the complexities associated with the Microservice architecture (MSA).
Issues
Below are some of the issues associated with the MSA and should be addressed before starting out. Take for example, your team is about to build a system to for process mortgages. This application consists of several components. Say we have the following services:
- Authentication Service
- Mortgage worthiness Service
- Notification Service
- Repayment Service
Below are a few things that should be taken care of before building
- Database Structuring
Each service has to persist data into some form of database....right? So how is this done in a microservice architecture?
The Database-per-service pattern can be the solution to this. This pattern allows for each service to have it's own separate database or at least have sole access to it's own tables or models in the database.
If a relational database is being used across all services, the database-per-service pattern could be implemented in three ways, namely:
Private-tables-per-service: Each service owns a set of tables that must be accessed by that service only.
Schema-per-service: Each service has its own database schema that is private and only accessible by that service.
Database-server-per-service: Each service has its own database server.
Advantages
Data storage requirements might differ for each service. Some services might be well suited for noSQL databases while other might thrive using SQL databases. Using the Database-per-service pattern makes this decisions easier.
Ease of scalability: There are cases where databases need to be sharded and replicated for scalability.
Note: The private-tables-per-service and schema-per-service have the lowest overhead. For these patterns to work, there must be a clear protocol enforcing this pattern so as to prevent developers from bypassing the service and calling tables or schemas directly from other services.
In cases where some queries must join data owned multiple services, data owned by each service should only be accessible via its API.
- Routing Pattern
How would client requests be routed to each services? There are two possible routes.
1. Client-side Routing: The front-end could make calls to each service to retrieve data needed. This however puts a lot of load on the front-end because several calls would have to be made for data dependent on multiple services.
Another drawback with this approach is that it makes it difficult to refactor the services. Over time we might want to change how the system is partitioned into services. For example, we might merge two services or split a service into two or more services. If, however, clients communicate directly with the services, then performing this kind of refactoring can be extremely difficult.
2. Server-side Routing: All client requests could be routed to one API gateway which acts as a single entry point into a system. The API Gateway is responsible for request routing, composition, and protocol translation. It provides each of the application's clients with a custom API.
This single entry point will then handles other requests and aggregate results from multiple back-end services (If needed). In order to minimize response time, the API Gateway should perform independent requests concurrently.
In a case where a request requires a response that would be fetched from multiple services, there should be a graceful way of handling service-specific errors. For example, assume a request expects a response that contains the details of a customer and the customer's transaction history. However, the transactions service is down or times out, a graceful way of handling this is by returning a null or an empty array for the transaction history key.
The API Gateway can also handle failures in the back-end services by returning cached or default data as response.
- Deployment Strategies
Services could be deployed:
Per host
Per VM (Virtual Machine)
Per container
NOTE
Each service in the MSA should be independently deployable and scalable.
Instance of each services should be isolated from one another
The behaviour of each service instance should be observed and monitored.
Conclusion
In conclusion, when breaking an already existing monolith into microservices, it is a good practice to audit the current system and try to maintain the same response body as the initial system (this is very helpful in the case of backward compatibility especially with mobile apps, as users don't put conscious efforts into updating their apps).
Also, it might be of use to enforce communication rules and an internal authentication mechanism amongst services. For example, each service should have a registry that contains the aliases and auth keys of other services it would be in communication with. This would guide against reckless/unstructured communication amongst services.