It’s been a few years since the hype around microservices began, resulting from a perfect storm of pressure to meet digital transformation initiatives and accelerated time-to-market to remain competitive.
In the midst of this reality, modern corporations were faced with a major dilemma: do they buy new applications even if they don’t meet all their needs? Or, do they develop for innovation and a perfect fit with existing systems to protect their past investments?
No matter what the decision, there is a need for custom development and integration. The result is an extensive portfolio of applications and integrations with other vendors and technology that supports the business—a portfolio that too many times needs considerable maintenance. So, the focus moves from delivering products to dealing with all these applications coherently.
This time-consuming effort has created the ideal conditions for a decoupled service strategy--namely microservices, in its most popular format.
But, are we all to follow the examples of Netflix, Amazon, and eBay or is there still room for monolithic architectures to shine?
Are Microservices for Everyone?
In a microservices architecture, the functionality of the application is broken up into smaller fragments to make it more resilient and scalable.
One can undoubtedly understand the benefits of these self-contained services. Multiple teams can have independent continuous integration/continuous delivery (CI/CD) pipelines while capitalizing on different skills, vendors, and technology.
Plus, microservices addresses a bunch of the non-functional requirements (NFRs) that a project should cover in order to meet quality requirements.
However, it is essential to understand when it pays off. After all, not all companies require that many teams or that complex of a team setup.
Monolith vs. Microservices: When to Use What
In the moment you choose between a monolith or a microservices architecture, there’s no one size fits all. In a recent Tech Talk, about this exact topic, my colleagues Hugo Fragoso and Arjan Waardenburg, use the example of an app modernization initiative to explain when microservices are a good solution, or when sticking to a monolithic approach is preferable depending on the use case you’re facing:
Use Case 1: Modernize the UI of Your Core System
Scenario: Your core system still works well and provides an appropriate API, so you simply want to modernize the UI to abstract the end-user from the IT complexity.
In this scenario, there’s no need to go into microservices. Adding a new UI layer is a quick fix, and you’ll even benefit from keeping a monolithic architecture on the service-side.
Use Case 2: Replace Part of Your Core System
Scenario: Your core system still has valid functionalities that you want to keep, but there are other components that have become obsolete and it’s time to replace them.
This is when you can start breaking your monolithic architecture into microservices to introduce the new services that will replace the outdated ones.
Use Case 3: Multiple Business Applications and Need for Strong Reuse
Scenario: Your software factory starts to grow and you have multiple business applications that use some common rules.
In this scenario, you can start introducing modularization into your software factory. This way, instead of creating each app from scratch, a microservices architecture will help you introduce and support reusability in your software development.
Use Case 4: Independent Teams
Scenario: Companies with huge products and big development teams.
This scenario is more common in a complex software scenario that involves multiple teams, and this is when microservices are the go-to answer. When companies have big products, like Netflix or eBay, that involve hundreds of developers, the segregation promoted by microservices is beneficial so that those domains and teams are decoupled with different release cycles.
Now, let's take a look at what you get with different approaches.
Monolith vs. Microservices: What Are the Tradeoffs?
In a monolithic approach, all application components are tightly connected using the same technology, which dramatically simplifies the implementation of business transactions that require the chaining of multiple services. The tradeoff is clearly on the generated dependencies that become more difficult to deal with in more complex systems. This not only makes a CD/CI strategy almost impossible, but it also makes it painful to scale the solution due to code complexity and long deployment times.
In a decoupled architecture that follows microservices principles, application features are composed by calling self-contained services. These can be implemented by different teams at their own pace, scale independently, thereby reducing the complexity of the solution. They are mostly independent of vendors, products, and technology, using a communication protocol over a network. However, the decoupled service architecture comes with a significant invoice for data transaction consistency and resilience in the fact of communication hiccups.
The fact of the matter is that both approaches have their highs and lows. Because, yes, a monolithic architecture is not a good approach in the long run, but it can jump-start app development in a way that microservices cannot.
So, maybe the solution is somewhere in the middle—an approach that takes advantage of both by maximizing the merits and minimizing the weaknesses.
Creating a Third Way
Making good decisions about your architecture is key to the success of your apps, and this is only achieved with a complete understanding of the benefits and costs of the available approaches. It’s in these moments that the concept of Domain-Driven Design (DDD) introduces interesting criteria to guide your decisions.
DDD drives the development of complex systems based in decoupled domains of technology artifacts. What does this mean? You can define domains that are oriented by the line of business or by CI/CD pipeline, thereby providing a highly independent ecosystem that your teams can manage at their own pace and needs.
For a domain-driven approach to succeed, it’s critical that you clearly define what a domain is in the context of your company so that you can apply the two most important rules of this approach:
- Strong coupling within the domain. All the services within a domain that are exclusive to it can and should be strongly coupled. Your transactions will be faster and can scale without jeopardizing the health of the architecture.
- Loose coupling across domains. Use an API for independent resource consumption across domains.
Another important aspect of this approach is the definition of domain. You have two types. Vertical domains support applications for a LOB or product family. Horizontal domains typically support your foundation services. These are the services that are reused across the vertical domains.
The nature of their loose connections allows for vertical domains to provide a limited amount of services without compromising independence.
Domaining the World
By defining your domains correctly and following these two rules, you get the best of two worlds. You will have an architecture that is highly reusable but at the same time highly efficient.
Are you thinking, “So, what’s the catch?” Here’s my simple answer. Defining domains and setting independent teams is not always easy for some companies. Many times, the lines of business are not clearly delineated; at other times, your architecture is so strongly coupled that the investment to decouple it is huge. In this case, some decision-makers may be afraid to take this step. There are lots of potential barriers.
But at the same time, you can attain these benefits if you are willing to tackle the barriers and fight the resistance to change. This is the true challenge of any digital transformation.
To learn more about when to use a microservices approach versus a monolith, and how to define your domains following Domain-Driven Design, I invite you to watch the webinar Head-to-head: Monolith vs. Microservices.