I came across this article from Robert C. Martin (Uncle Bob) about Microservices architecture. In it he argues that any architecture whether microservices or traditional 3-tier or anything in between, needs to be designed to be decoupled from deployment strategy. He argues that the code should be unaware of whether it will eventually be deployed as microservices or DLLs or anything else. And that you shouldn’t bake deployment strategy into your code. Interesting article for sure. You can read the complete article on his site here.
I have been interested in MicroServices architecture for a while, and came across this wonderful article on EnterpriseIntegrationPatterns.com. Given that I spend most of my work day at Starbucks hiding from my kids and single-handedly trying to bankrupt Starbucks with their unlimited coffee offer for gold members (Oh Behave!), this article hit close to home.
It presents a very interesting real world analogy on asynchronous conversation happening between services, in this case a customer, a cashier and a barista. Each of these could represent a service talking to each other in an asynchronous fashion. In a non-asynchronous approach, all three parties would be tied until the transaction was complete. This would definitely lead to longer wait times, reduced profits and an overall inefficient system.
Here are a few of the problems from an architectural perspective with this approach:
- The customer and cashier services are both tied up for the entire duration of the complete transaction. The cashier service is unable to do anything else until the current customer’s request is completed.
- The barista service is not being fully utilized and is waiting around doing nothing while customer and cashier services are busy doing business that has nothing to do with the barista service. Meanwhile, there may be other customers waiting in line. Note that adding more instances of the barista service would not help.
In comparison, imagine a microservices based architecture, where each of the services is communicating via an asynchronous messaging bus. Clearly, this is a more efficient system where each service is only tied up doing what it does best. It receives a request, carries out its function, and places a response back on the bus. Here are the clear advantages of this approach.
- The customer service is not tied up for the entire duration of the transaction.
- The cashier service is also not tied up for the entire duration of the transation, and further can service the next customer request.
- The barista is not tied up waiting around while the cashier and the customer chat it up, and is able to work on the next task as soon as it comes in.
There is nothing monolithic vs. microservices that we have discussed here so far. So let’s see how that might look in comparison to each other if a lot of orders started coming in. Both architectures can handle this, but they way they do it is different.
Monolithic architecture can scale horizontally, which would mean we could deploy additional instances of our cafe application comprising of the cashier and barista libraries. A load balancer would then assign incoming requests to one of the many instances of our application. In most monolithic applications, you would have to deploy the entire application i.e. for each additional barista, there would also be an additional cashier. In case of a cafe, this would obviously be not great for business as you would have to pay the salary of an extra cashier. In an enterprise solution, this may mean deploying and maintaining additional resources and other possible overhead. Also, maybe a cashier’s average task length is 2 minutes, whereas it takes a barista 4 minutes to make a drink. What if a customer ordered 10 drinks and the cashier still takes only 2 minutes to take the order, but the barista will now take 20 minutes to make all the drinks.
Vertical Scaling in Microservices
This is where a microservices architecture would shine in comparison. Since the cashier and barista services would be developed and deployed individually, you can easily scale these services vertically. So you can have 10 instances of barista service running, while perhaps only two of the cashier service.
Decentralized Design Choices
The last point in favour of microservices in this case would be that since microservices are independently built, deployed and scaled, it is also possible to design the cashier and barista services differently. The cashier service may use a traditional relational database such as SQL Server or Oracle to store customer and order data. Whereas perhaps the barista service may benefit more from a NoSQL key pair type database where each drink id correlated to its ingredients and recipe. This flexibility in design choice of each service, is precisely the purpose of a microservice architecture.
The software architecture world seems to be moving towards microservices style of architecture for some of the reasons explored in this article. While it is definitely worthwhile to consider, there certainly may be applications, where a traditional 3-tier monolith application may be just as worthy a choice, if not preferable. One instance of this would be where the out-of-process cost of interservice communication in a microservices architecture may be too much to bear, and may be disregarded in favour of traditional in-process function calls inside interdependent libraries used in a monolithic application.