Building applications in Monolithic architecture includes a client making request, server (with router, authorization middle-ware, some set of features, business logic) and a database. The whole App can be put into place using these. The build artifact is a single executable hosted on a single VM with consistent a technology stack.
In a Micro service, this set makes only a single feature for the app. They work independent of each other without any direct dependency on each other's database. If any of the service is down, the app still works. The services are small, autonomous, and independently deploy-able.
Monolithic can work well for small applications but even a single line code change means downtime, and it cannot be easily scaled horizontally (add new services) and can only be scaled vertically (means more processing power).
- Small Services It can be owned by a team, easier to understand and rewrite.
- Technology Choice Adopt new technology, use right tool, Standardize where it makes sense.
- Individual Deployment It has Lower risk of app failure, no downtime, frequent updates
- Scaling It can scale services easily, cost effective
Why do all the hard work to create many different code bases and use heterogeneous technologies to create an app?
There are many challenges in micro services too, for example the communication with each other. The interactions are complex if not avoided can be inefficient due to web of requests b/w services.
In micro services we follow two rules strictly:
- Each service gets it's own database (if it needs one) This is called Database-Per-Service pattern, we do it because if we use only single, and that db is down, the whole app comes down, the Single Point of Failure needs to be avoided, and secondly is scalability, it is a lot easier to increase capacity and throughput of databases as per needs of each service.
- Services will never, ever reach into another services database If anything ever goes wrong with database of dependent service, other service also gets lost, secondly, If schema of one database is altered, both service would need to be updated. we can also use different types of databases best suited for specific needs.
Let's try to visualize how would it work and find solutions to raised challenges,
Here's an example of the app with these 3 features:
- Users can sign up
- User can submit posts
- User can comment on each post
But now if we want to add another code that can list comments for post of a particular user:
- we need user from users collection
- we need to find posts of that user
- we need to fetch comments of that post
In monolithic server we can reach out to each database and fetch required information. Here's how it would look:
But this pattern is very inefficient, we'll see in a while.
By going by Database-Per-Service pattern in Micro services, We can add another service that can do this work for us:
How will it reach out to three separate databases of different services? This is not allowed in Database-Per-Service pattern. To figure this out we will understand how to establish communication between services.
There are two general strategies to establish a communication strategy between services:
- Synchronous Communication Services communicate with each other using direct requests
- Asynchronous Communication Services communicate with each other using events
One service can communicate with another service using a direct request, this may not need to be HTTP, it could be any type of request. In our case to request comments of a post of a user, The service D will make 3 different requests to each of other service.
- Easy to reason about and add new service straightforward
- New services don't need a database
- The entire request is only as fast as it's slowest request. For eg: if request 1 takes 10ms, request 2 takes 10ms but request 3 takes 100ms, the response would time would be more than 100ms
- Makes service dependent on each other, if any service goes down, the entire service goes down
- Difficult to track requests due to multiple nested requests.
This type of communication would need an Event Bus which can emit and receive events, that will be connected to every service in the application.
This decouples the services from each other. Instead of one-on-one communication, they talk to each other using a message broker. If other service is down, the first service can still work and second one presume itself later. There are two types of messages: Commands ("Do this please") and Events ("Something happened in Past").
Now in our case service D would first broadcast an Event (UserQuery) to every other service, those services will handle the event if they want and can again release event for result for that Event. From that User received, Service D will again send a PostsQuery, and then finally from those Posts, another event CommentsQuery to Event Bus. Now Event Bus will broadcast each event to every service until service D will receive the result.
This approach is very bad and has all the downsides of synchronous communication as well it many of it's own.
A better approach would be to add a database that can serve the required information. Other services will emit events and populate this database, now this database will be ready to serve the request instantaneously.
- Service D has zero dependency
- The queries are very fast
- Hard to understand and code
- Data Duplication
- Extra Storage cost (but cheap!)
Thanks for making it to the end of the post, you are awesome!
You just took the first step in seeing the application architecture from a high level perspective. There are tons of information out there to learn more on this. Don't forget to leave your thoughts. I got this information from this awesome Stephen Grider's course, Here's the non-affiliate link (https://www.udemy.com/share/102VKE/).
Please share it if you found it helpful or drop me a hello on twitter :)
Follow up read -
Level up every day