Microservices. To some, the word has a melodic ring to it; to others, it buzzes like an annoying mosquito you want to swat away. Microservices, also known as microservice architecture, isn’t something that you must dive into immediately—but it’s also not something you should ignore. In this post, I explore the pros and cons of microservices to help you decide whether to embrace this trendy software architecture style or swat it away.
Breaking Down the Monolith
Imagine that you own Hat on the Cat LLC and you’re in the business of selling hats for cats. You’ve recently released a new product recommendation feature and have discovered a major issue with your ordering functionality. Every single order is being recorded as a unicorn hat, and cats everywhere are furious.
Here’s a look at your current system for Hat on the Cat:
In the microservices world, what you have here is a “monolith” architecture. It’s the opposite of a microservice, but it’s not a bad design. Both the user interface and the shipping department use the same API, which stores information in a single database.
However, to fix the bug, you need to change the ordering functionality and deploy the entire API—a lengthy and somewhat risky process that could affect other business functionalities. You could (or should) run a suite of unit tests and functional tests to verify nothing else has broken. But these tests may not catch everything, so your deployment could still be at risk.
Once the deployment is over, you still have to inspect the results: Did the developer fix the ordering bug only to introduce a bug in the shopping cart software? Is the fix—which the developer promises worked in their dev environment—still broken in production? If so, we must rinse and repeat this vicious cycle.
Finally, here’s another consideration for the monolith architecture: What happens if there is a catastrophic failure in the API? What if there is a bad memory leak or network issues? In this scenario, your application is doomed. Your API becomes unavailable and stops working; users can’t register or log in; customers can’t place orders; and the shipping department can’t ship products.
But what would happen if you had microservices instead of a monolithic architecture?
The Good: Microservice Pros
Here’s how Hat on the Cat would look with a microservice architecture:
Instead of one API, you have many smaller APIs, or microservices. With this setup, you only need to adjust and deploy the Ordering API in order to fix the ordering bug. And, since there’s a smaller set of tests to run, you can deploy much faster without jeopardizing other functionality. Additionally, if your Ordering API suffers a catastrophic failure, your other services will continue to work; users can still register or log in, they can browse products, and the shipping department can continue shipping.
However, when migrating to microservices, it’s often difficult to get the granularity of your services just right. Typically, one may start with more coarse-grained services and then break them into fine-grained services as the need arises. For example, rather than constantly making calls directly to the database, your services may use caching to improve performance. When you split your monolithic API into microservices, you add caching logic to each service; but doing so duplicates logic and adds complexity to each service. The solution? Create a microservice solely for caching, which your other microservices can use. This removes the complex caching logic from each service, making them smaller and more manageable. With the cache microservice, your system architecture will look like this:
Now, let’s talk about fine-grained services. Say your product service is composed of product catalog functionality, which is responsible for adding new products, retrieving active products, and product recommendation functionality. While your product catalog logic is stable, your product recommendation logic—which suggests additional products that customer may be interested in—is frequently modified due to bug fixes and algorithm updates. This may indicate that you need to split the product recommendation functionality into its own service so that you can continue making modifications without affecting your product catalog logic. In this case, your updated system architecture will look like this:
As you can see, your services are becoming more fine-grained—but separating frequently modified code from stable code is only one reason to break apart a new service. So, let’s look at another example: Let’s say Hat on the Cat has grown to a million visitors per day. While hundreds of thousands of visitors add items to their shopping carts, only a small percentage of them check out, so that traffic strains your shopping cart functionality. In your current system, the shopping cart logic and checkout logic are both in your ordering service. By separating these two functions, you can remove the strain on your shopping cart functionality without affecting checkout functionality. Furthermore, on auto-provisioning services such as Azure and Amazon Web Services, you can resolve these strains and manage costs by configuring microservices to scale on demand. With this setup, your system is even more fine-grained and looks like the diagram below.
The diagram above outlines eight microservices and eight databases, which are loosely coupled. This means that, instead of adding a dependency to the assembly, they communicate with each other through events or HTTP calls. Loosely coupled microservices and databases allow for technology diversity, or freedom to use whichever data storage solution is right for a specific service. For example, while a relational database like Microsoft SQL Server may be optimal for the product catalog service, a document database like Redis might be a better fit for the cache service. Each service may also be written in whatever language the development team prefers. However, whether or not this is advantageous really depends on the size and culture of your organization, as well as the structure of the teams working on the microservices. It’s up to the organization to decide whether to allow technology diversity or not.
As you can see, there are quite a few pros to microservices architecture—but let’s not jump on that micro-bandwagon until we talk about the cons.
The Ugly: Microservice Cons
In his blog post, “MonolithFirst,” author Martin Fowler said, “Microservices are a useful architecture, but even their advocates say that using them incurs a significant MicroservicePremium, which means they are only useful with more complex systems. This premium, essentially the cost of managing a suite of services, will slow down a team, favoring a monolith for simpler applications. This leads to a powerful argument for a monolith-first strategy, where you should build a new application as a monolith initially, even if you think it’s likely that it will benefit from a microservices architecture later on.”
In other words, microservices are not a golden hammer. If your system is simple, you probably won’t need microservices. If you’re building a complex system, you should still consider a monolithic-first approach. Then, after the development team understands the business requirements and the contextual boundaries around the system better, they can slowly migrate to a microservices architecture.
Also, another downside of microservice architecture is the increased difficulty of programming a distributed system correctly. To complete tasks, your developers will have to implement orchestration and/or choreography—two potentially risky approaches. To explain orchestration and choreography, let’s imagine that a customer places an order using our Order API. Now, the Shipping API needs to create a shipment record for this order. Using orchestration, the Order API would make a remote call to the Shipping API, providing it with the order details. Using choreography, the Order API would publish an event that notifies subscribers that an order was placed, and the Shipping API would subscribe to that event. However, both approaches can fail, and those failures would need to be managed. After all, if an order is placed and never shipped, that’s a big problem.
Another consideration is the increased effort for deployments and monitoring. Instead of dealing with a single deployment, you now have to manage several. Also, each service will have its own logs and metrics, which makes it difficult to monitor system performance and potential malfunctions. In this case, automation and aggregation are your best friends here—in fact, I’d suggest that you avoid a microservice approach unless you’re willing to have automated deployments. I’d also suggest that you aggregate all your logs and metrics into a centralized place, so that you can easily monitor your entire system.
Microservices or Monolith—What’s Right for You?
Microservices have many benefits, such as rapid deployments, fault tolerance, technology diversity, extreme separation of concerns, and the ability to scale small portions of the system—but those benefits come at a cost. Sam Newman, in his book Microservices: Designing Fine-Grained Systems, stated, “Microservices are no free lunch or silver bullet, and make for a bad choice as a golden hammer.” And he’s right, microservices aren’t great everywhere—but they are excellent at making complex systems more manageable and less fragile. So, deciding on which architecture is right for you ultimately depends on the complexity of your system.
Wondering if you are a good candidate for microservices? Contact AgileThought to learn how we can help you decide on, and implement, an architecture that’s right for your business.