Developers have been developing applications for years now. Of course, we’ve become better at it, and we’ve developed new ways to structure architectures. Recently, microservices architecture has gained a lot of popularity, as it better enables an agile approach to development. The microservices trend has emerged out of various business needs such as continuous delivery, continuous integration, and a massive need for scalability while building an understandable application architecture. Therefore, microservices formed the solution in which we follow a domain-driven design style for structuring small, independent services.
This post will explore the following questions:
- How do I know if using microservices is right for me?
- What are the benefits of implementing a microservices architecture?
- How to design your microservices architecture?
Let’s first have a quick refresher on microservices by exploring a simple use case.
What Are Microservices?
Microservices architecture is an architectural style for structuring an application as a collection of different independent services. Each service is focused on a single responsibility and only performs tasks related to this responsibility. Robert C. Martin has coined the term single-responsibility principle (SRP), which refers to the following definition: “Gather together those things that change for the same reason, and separate those things that change for different reasons.” In other words, each microservice is good at performing a couple of functions related to a single feature.
Next, those independent services can communicate via a REST API that each service exposes. This is one of the most common setups for communication between microservices.
To get a better understanding, let’s explore a simple use case of a microservices architecture.
Microservices Use Case
To get a different view on microservices, let’s explore a simple use case. Imagine a shop that sells flowers online. The owners don’t want to sell flowers that are out of stock, so the shop keeps track of the remaining stock for each type of flower.
A traditional web application handles the following operations via a single back-end service:
- Manage basket for each user session
- Handle payment process
- Update stock when a user buys flowers
- Store user information
However, what if our digital flower shop becomes as popular as Amazon? We’ll need to scale our application to meet the demand.
We can try to scale our application using tools like Kubernetes. This works, but there’s a more scalable solution. It would be better to redesign our single back-end service into a microservices architecture. Therefore, we need to think about the single responsibility principle and split up functionality. If we do this, each of the above operations will become a microservice, which allows for better scaling. Now, we can scale every service independently.
For example, we see a high number of users who add items to their shopping basket. This sudden spike in activity requires us to scale up the number of basket management services. Note that we don’t have to scale any of the other services. This means we can scale individual services more efficiently instead of scaling the entirety of our traditional back-end service.
Benefits of Microservices
There are many clear benefits of adopting a microservices architecture. Let’s explore five important reasons why you should consider this move.
1. Onboard Faster
This benefit can be interpreted broadly. A microservices architecture enables a new team to become productive faster. They can start from a simple boilerplate for each microservice and quickly develop the needed functionality. There’s no need to spend days designing the architecture for your traditional monolith application that holds all logic.
Besides, new team members can onboard swiftly as services are independent, small, and easy to understand. Therefore, a new team member doesn’t have to invest a lot of time into understanding the whole codebase. In other words, there’s less technical debt. They can get started quickly creating a new independent service or modifying existing microservices.
In short, microservices, by nature, are easy to understand.
2. Implement Continuous Deployment and Become More Agile
Microservices allow developers to rapidly develop new features that can be tested quickly. To test those new features and receive fast feedback, microservices work well with continuous integration (CI) and continuous deployment (CD). The combination of CI/CD allows your team to support the agile nature of microservices and reduces the stress on your developers. Therefore, you can quickly ship new features to your production environment, reducing the time to market. This is an essential benefit for startups that need to deliver new features fast.
3. Have Increased Resilience
As microservices follow SRP, each service is independent. This means that an issue with a particular service shouldn’t affect any other services. With a traditional monolithic architecture, where all services are grouped in one shared code repository, there’s an increased risk your whole service will crash when an issue occurs. Therefore, it’s fair to say that a microservices architecture is more resilient.
4. Reduce Costs Through Effective Scaling
Lastly, it’s important to understand how microservices can reduce your bill at the end of the month. As discussed before, a microservices architecture allows you to scale your application more effectively. Instead of scaling your whole monolithic service, you can scale individual services that need more capacity to handle the load. This can have a big impact on your cloud costs at the end of the month.
5. Benefit From the Agnostic Property
Microservices are independent and therefore also agnostic. This means that it doesn’t matter which programming language you use to develop your services. However, don’t abuse this property by developing each service in a different programming language. This defeats its benefits, as your team needs to understand each programming language to be able to maintain those services.
Moreover, use the agnostic property correctly. This allows your team to choose the right language for the right functionality. For example, for a service that involves heavy data processing, you can choose to use Golang instead of Node.js. Another example would be where a certain tool only supports a particular language.
Next, let’s answer the question about when to choose a microservices architecture.
When to Use a Microservices Architecture
When first starting with microservices, you should ask yourself whether you really need a microservices architecture? Some might find this question stupid, but I’ve seen many implementations where a traditional monolithic setup might have worked better.
Of course, microservices are trending, and this makes many organizations shift towards this new approach. That’s totally fine. However, a startup that’s just working on the first version of their product doesn’t require the added scalability benefit of microservices. Here, a lot of time can be saved by not implementing a microservices strategy, as you don’t have to spend a lot of time on your CI/CD, designing responsibilities, or communication between services.
In a later stage, you can still convert a monolithic application to a microservices architecture relatively easily. Put differently, for more evolved startups or scale-ups that already have a stable userbase, it makes more sense to implement microservices to focus on future scalability.
In other words, time your move to microservices correctly. When you decide upon this move too late, it might be very difficult to untangle services and their dependencies.
How to Design Microservices
The first and foremost rule when designing microservices is SRP. It’s one of the most applied and famous rules for writing high-quality code. Also, for microservices, this acts as an important rule to design a high-quality microservices architecture.
The idea of SRP is to avoid designing a service that handles too many different things. Such a service is bound to fail sooner or later. For example, a user profile service that’s responsible for all of the following actions will likely fail:
- User authentication
- User verification
- Profile retrieval
- Profile modifications
As you can see, this service performs too many actions. According to SRP, a service should only perform actions related to one specific functionality. For example, the above actions can be divided into two services.
- Service 1: User authentication, which includes user verification.
- Service 2: User profile-related actions such as creating, updating, retrieving, and deleting user profiles.
Next, let’s explore different approaches to implement microservices according to SRP.
Different Approaches to Implementing SRP
There are different approaches to SRP. For example, you can design services around nouns. You can have a goods service that handles all requests related to adding or modifying information about goods. Alternatively, you can design an account service that handles all account-related requests.
However, this strategy does not apply to every use case. In some cases, you might end up with a service that handles way too many tasks, even though they’re all related to the same noun.
To clarify this, let’s explore the account service we just mentioned. Such a service can become very big quickly if you consider the following account actions: manage user access or store user profile pictures. Here, we have to integrate our service with an LDAP tool to manage user access or integrate with a cloud provider such as Amazon to store user pictures. As you can see, a simple service can quickly become very complex. In this case, it would make more sense to have a single service dedicated to handling user access rights or user profile updates.
In other words, it’s not fair to propose a single golden rule you should always follow. Every use case is different and might require another strategy for designing your microservices architecture. Therefore, let’s explore five strategies for designing your microservices architecture.
1. Structure by Business Needs
Structuring your microservices architecture around business needs depends on the stability of your business needs. If your business needs change frequently, this isn’t an ideal architecture.
To continue, a business need refers to the core capabilities of your organization. Let’s say your organization focuses on selling products online. Therefore, your core business needs include
- Product management
- Inventory management
- Order management
- Delivery management
These are business needs that won’t change unless your organization decides to completely change its core business, which shouldn’t happen.
Further, you can design specific actions for each business capability. Here are some examples of actions for the “product management” capability:
- Update product description
- Add a new product to catalog
- Remove a product from the catalog
- Change price for a product
In short, this allows you to develop a stable architecture around core business capabilities. Moreover, it allows you to employ cross-functional teams that are focused on a specific business capability.
2. Structure According to Domain-Driven Design
Next, you can follow the domain-driven design (DDD) principles to create your microservices architecture. Each domain represents a business asset or capability. Further, each domain consists of multiple subdomains that perform actions related to this domain.
If we take our online shop example, we can design a product domain with the following subdomains:
- Order management service
- Inventory service
- Product catalog management service
- Delivery service
The above subdomains have one thing in common: They all require product data in order to function properly.
Furthermore, DDD allows you to define relationships between domains to have a deep understanding of data connections and the flow of communication. However, it’s not easy to implement DDD correctly. You need a person who has a deep understanding of the business to help you design a domain-driven design for your microservices architecture.
3. Structure by Verb
Third, we can design a microservices architecture by a verb. For example, an online store can have a checkout service. This checkout service handles all actions related to the checkout process. However, structuring your microservices by verb is not an easy strategy, as it’s hard to encapsulate every service by a single verb. As we saw in the previous example for our online store, we can define multiple services related to product management.
In short, for some specific use cases, the strategy of structuring by verb might work well. However, for most other use cases, this strategy can introduce unwanted coupling between services, as it’s not possible to decouple by verb. Therefore, be cautious when using this strategy.
4. Structure by Noun
Structuring by noun is a more popular strategy for designing your microservices architecture. This is a good strategy for organizations that have to manage many different assets or resources. For example, some service company can have the following services structured by nouns:
It’s a fairly easy strategy, as it’s much easier to define the assets your organization manages. However, there’s a risk that some nouns grow too big. As illustrated with the account service, this service can quickly become very large, which isn’t ideal for a lightweight, decoupled service.
Alternately, you can choose a mix of verbs and nouns to structure your microservices architecture.
5. Structure by Nouns and Verbs
Lastly, you can structure microservices by both nouns and verbs. As mentioned before, a user service can perform a lot of actions and needs to be reduced in size. Here, we can split our user service into the following services:
- User verification
- User authentication
- (Optional) user management
In other words, the nouns and verbs strategy is ideal for decoupling services that grow to big.
Drawbacks of Working With Microservices
Now, let’s learn why microservices just won’t work for some organizations or use cases. First of all, microservices help your team to go agile and become more productive. However, this comes with added complexity. Your team needs to make sure your DevOps implementation supports microservices.
Managing a Distributed System and Teams
Furthermore, microservices add a lot of complexity, such as managing data integrity when multiple services access the same data sources. Also, there’s the added complexity of managing a distributed system. Tasks like unit or functional testing become simple, but running end-to-end (E2E) tests can become a real challenge to get right.
Also, make sure your team is ready to work with the correct mindset for microservices. Teams need to be fully aware of the responsibilities of each service to avoid building coupled services. Coordination and communication between multiple cross-functional teams are therefore very important. It’s not an easy task to adopt a microservices mentality when your team has been working as a single team in a monolithic application setup.
Tools to Address Debugging Complexity
Lastly, debugging an issue in your microservices architecture can quickly become difficult. In the end, you’re trying to debug a distributed system. If you don’t have any log aggregation tools in place, you’ll have to look for clues in the logs of each involved service. However, this problem can be avoided by using a log aggregation tool such as Scalyr. Scalyr helps you to aggregate logs, which makes it much easier to trace logs or find logs for a specific user action. This reduces the resolution time for bugs, which is an important metric.
To summarize, weigh the benefits and drawbacks of migrating to a microservices architecture. You don’t want to end up in a situation where you spend weeks of work migrating your application only to find out that a microservices architecture doesn’t work for your project.
The most important question you should ask yourself is this: How can I decompose my application into services?
To answer this question, you have five possible options. Each strategy has its benefits and drawbacks. For some use cases, multiple strategies apply. For example, an online shop can be designed using the business needs strategy or using domain-driven design. Again, I want to express that you’re free to choose how you structure your microservices, as long as the way your design your architecture doesn’t violate SRP.