19 September, 2019
The microservices architecture is one of the hottest trends in software development. As the CTO, you need to know when to use them. But you also need a deeper knowledge of the topic to really get hold of your project. By learning more about design patterns in microservices, you will learn exactly how microservices work and how developers can make them more efficient, scalable and secure. Meet the most popular microservices design patterns.
In the previous article on microservices, we introduced the basics of this popular software architecture. With that knowledge, you know what kind of projects microservices are best suited for. But once you decide to go for it, there will be even more decision to make. That’s why you should learn about design patterns.
What are design patterns in microservices?
As you already know, a microservice is a largely independent application component tasked with a specific function in a system. Multiple microservices, each taking care of another function of an app, combined with clients (e.g. the frontend of your web and mobile apps) and other (optional) intermediary layers make a microservices-based architecture.
This type of setup has many advantages, such as the ability to write any service in a different technology and deploy them independently as well as performance boost and more. But it also comes with quite a few challenges, including complex administration and configuration.
Design patterns exist to address such common challenges in microservices and provide proven solutions to make your architecture more efficient and the entire administration process – less costly and troublesome. As a result, learning about them is a great way to understand microservices better – more high-level than actual coding, yet specific enough to figure out the inner workings of microservices.
Interested in developing microservices? 🤔 Make sure to check out our State of Microservices 2020 report – based on opinions of 650+ microservice experts!
Why should you learn about design patterns?
Choosing the right design patterns can literally make or break your microservices-based project. They are the best proof that microservices alone are not a cure-all, and to really benefit from them, you need to do them right.
If you DON’t care about microservices design patterns:
- your app may perform badly (due to unnecessary calls and inefficient use of resources),
- the whole system will be unstable (e.g. connecting and integration issues),
- it may face scalability issues (adding more services can cause difficult-to-maintain dependencies, which may even turn it into a de facto monolith),
- it may compromise security by exposing the endpoints of your microservices to the public and by other means.
- you might have a lot more maintenance and debugging work to do than you could with better preparation.
Types of microservices design patterns
Design patterns in microservices exist in pretty much every aspect of the architecture. Some of the most important ones could be divided into areas such as:
It concerns the methods of communication between microservices and client apps (the frontend layer).
These design patterns constitute various ways microservices can communicate between each other.
A variety of security-related concerns, such as the organization of the security layer, authorization and level of access to particular microservices for different types of users, etc.
Ensuring that all microservices are ready to address the needs of the system (regardless of how intense the traffic is), ensuring the lowest possible downtime. This includes activities such as making sure microservices can restart on another machine, or whether enough machines are available, microservices being able to report their current states on their own, health checks and more.
It refers to the methods microservices use to find each other and make their locations known.
Setting parameters and monitoring the performance of the entire system to continuously optimize it as you go
In the subsequent parts of the article, we’ll focus primarily on the first type, going over the three most popular communication patterns – the direct pattern, API Gateway and Backend For Frontend (BFF). They provide an excellent opportunity to understand how microservices-based architecture actually works and the implications a developer’s choices have on its performance.
The direct pattern
This is the most basic setup for a microservices-based architecture. In this pattern, a client app makes requests directly to microservices, as shown in the picture below. Each microservice has a public endpoint (URL) the client can communicate with.
This is very easy to set up and quite sufficient for relatively small apps, but causes quite a few challenges that become more and more apparent and troublesome as your application grows in size and complexity:
- Performance issues
Even a single page of your app may need multiple calls for different microservices, which may lead to large latency and performance issues.
- Scalability issues
Because the client app directly references microservices, just about any change to the microservices can cause the app to break. This makes maintenance difficult.
- Security issues
Without an intermediary layer, the endpoints of microservices are exposed. This isn’t necessarily making an app inherently insecure, but it definitely makes it more difficult to take care of security.
- Complexity issues
What’s more, security and other cross-service tasks need to be included in each public microservice. If an extra layer existed, they could be included there, making all microservices simpler.
Since microservices are usually recommended for complex apps, there must be more scalable patterns.
There sure are! The API Gateway takes it all up a level. As described in the picture below, it provides an extra layer, a single entry point between a group of microservices and the frontend layer. It addresses all the concerns we just mentioned, by hiding the endpoint of microservices from the public, abstracting references to microservices from the client and reducing latency by aggregating multiple calls.
However, the API Gateway pattern is still not safe from scalability issues. It’s more than sufficient when the architecture revolves around one client. But if there are multiple client apps, the API Gateway may eventually get bloated, having absorbed all the varying requirements from different client apps. Eventually, it may practically become a monolithic app and face a lot of the same issues experienced with the direct pattern.
Therefore, if you plan for your microservices-based system to have multiple clients or distinct business domains, you should rather consider using the Backend for Frontend pattern from the start.
Backend for Frontend
BFF is essentially a variant of the API Gateway pattern. It also provides an additional layer between microservices and clients. But rather than a single point of entry, it introduces multiple gateways for each client.
With BFF, you can add an API tailored to the needs of each client, removing a lot of the bloat caused by keeping it all in one place. The result pattern can be seen in the picture below.
It’s worth it to mention that this particular pattern may still be extended for particularly complex apps. Different gateways can also be created for particular business domains. This model is flexible enough to respond to just about any type of microservices-based situation.
Does it mean that each microservices-based architecture should use the BFF pattern? Not necessarily. The more complex design, the more setup and configuration it requires. Not every app may require that. But if you want to create an ecosystem of apps, or plan to extend it in the future, you may choose a more complex communication pattern for the sake of future scalability.
If you want to learn more about BFF, make sure to read our Backend for Frontend case study – it’s a story of an app ecosystem reinvented using the pattern.
Other noteworthy design patterns
As I have mentioned before, design patterns exist in all aspects of microservices. Developers are often forced to make a choice between them, taking different factors into consideration. In some other situations, they can be combined or used alongside each other.
For internal communication, some of the most popular patterns include REST, gRPC, Message Broker, or the Remote Procedure Invocation. When it comes to security, the Access Control List (ACL) can be used for each microservice or each gateway, or as an independent microservice (or not used at all). When it comes to availability, we can use load balancing based on DNS or hardware. The service discovery can be performed on the client-side or server-side. As for configuration, it can be externalized (each microservice has one on its own) or centralized (all the configuration in one place). The list goes on.
See also: Practical introduction to gRPC
As microservices become more and more popular, new design patterns emerge to help solve various development challenges and make the architecture even more efficient. It’s worth it to know as many of them as you can, but it all comes down to choosing the right ones for your particular software ecosystem. When it comes to that – trust your developers, but make sure that you are aware of their choices and the implications they have on your software.
And if you are in the process of searching for developers capable of developing efficient microservices-based architectures, try contacting us. The initial consultations at The Software House are free of charge.