Recently in our company we decided to use Azure API Management service to improve security and scalability of our microservices. In this and following articles I will try to dive into the subject and show different aspects of how this service can help to improve your system architecture.
I will start by covering basic ideas and definitions. In the upcoming articles I am planning to cover security of services and CI/CD pipeline integration.
Azure API Management is a service that can serve as an API Gateway to your Azure services. Here I will briefly introduce the concept of Api Gateway Pattern. You can skip this part if you are already familiar with the topic. If you want to read more about the ideas and patterns - follow the links provided in description.
API Gateway is a pattern that aims to create a single-entry point for all clients, that hides the structure of microservices from the external world and that covers the common cross-cutting concerns so that there is no need to duplicate the logic across each service. It is often called as a “backend-for-frontend” and I personally thing that it is a perfect analogy. It helps you to write service without thinking of who and how will use them - client specific logic can be placed in APIM. Here you can find a great explanation of the pattern. I will also recommend following read that compares the direct client-to-service communication with API Gateway pattern.
The main benefits of using API gateway pattern are:
For a .Net ecosystem and Azure Cloud there are two main approaches to the topic:
The subject is wide, and decision will never be simple. In our case we decided to use Azure APIM service. We see drawbacks and benefits of this decision, but our journey has just begun.
The API Gateway is a new thing to our company. We all knew that we will need it sooner or later, in this or another form. There were few independent projects that were planning to employ this pattern. One of new projects was typed to break the trail. Here I will describe an evolution of sample project, created for the purposes of this article and inspired by the journey we traveled.
Please note that this is a sample application, although inspired by real story, it is simplified just to illustrate the problems, sometimes in naive way.
As a case study, imagine we have set of services and clients described below. It is an early stage and we expect it to grow fast in upcoming months. We expect new type of clients and services to come to the picture.
This can be illustrated by following diagram:
Even the system is small, we have all services coupled and clients talking to at least two of them. Note that on the picture Management and Statics service are grouped. That is because both have many common parts, and to speed up initial development, decision was made to implement them as single service with functionality split internally. Ok, I hope we can agree that we have things to improve here and that the current state, if not changed, will cost us a lot of pain later.
In following sections I will try to fix those issues by utilizing APIM. Of course, those problems can be fixed in other ways, but as you will see at the end - both API Gateway pattern and APIM service work nice to help make your microservice architecture cleaner.
As we have all our services hosted on Azure, we decided to use the Azure service called APIM (Azure API Management). First contact with it can be overwhelming. It is a huge thing, really. I got this task and after short time I thought that we are trying to use too sophisticated solution for a simple problem. It feels like an overkill! Fortunately, the more I worked with the APIM, the more I understand how useful it can be.
Here you can read how to create new APIM instance. After creating it, we can simply add our backends as separate Apis by importing swagger files. Let’s ignore products, subscriptions and other features of APIM for now. Let’s assume also that we are not doing anything else at the moment than adding new service between existing ones. We need to change the endpoints in our clients to point to APIM endpoints. After all, the end state looks like simple proxy between clients and backend services. Sounds like a waste of time and money (yes, APIM is not cheap at all). But let’s go further with that.
In services description we identified three microservices. To meet the clients, requirements, we have coupled Management service with both services. Let’s focus on the dependency on reporting service which looks like something unnecessary. Our Management Service is calling it only to get the list of previously generated reports for a given user. You may ask why Management Service is doing that, not UI directly? Let’s say that this was because of two main reasons:
It is a common situation in modern software development that different services can develop at different pace, by different teams and with different technologies and architectures.
We tested two approaches to decouple the two services:
The real power of APIM lies within policies. You can define variety of rules in different stages of request processing. Most of the things described in this article was done by defining proper policies.
We move forwards slowly. And we do not see a real power of APIM yet. You have probably a lot of doubts about what I did so far. I had them also. But please stay with me - the best is yet to come!
Now is time for different story. I mentioned that we have two other services: Management and Device Reporting. However, those were run by single project because of early architecture decision. To not complicate things, it was decided to implement them in single service, and different methods were spread across controllers. Even after few months of development we still can’t agree if this was good or bad.
Together or not, we must treat respective services differently:
Looks like we are in small troubles and we need some architectural changes here! And once more we utilized APIM functionality to resolve our problem. We created APIM api twice from same swagger file, called them DeviceAPI and ClientAPI and manually adjusted endpoints in each. Below diagram illustrates the current state with more details. Also, I joined two services together to highlight the fact that there are served together:
Then we were able to set up proper policies in each of the api. We applied access restriction, and what’s most interested - later on we applied different security rules to both endpoint groups. Furthermore, we can now change the way we implement the services, split them up or introduce new ones, without need to update client. Imagine we split our big service to few smaller services like illustrated below - no changes required from the client perspective!
There is also one more thing that is not clear from the pictures above. At this stage of application architecture, we have no direct dependency between any of the clients and any of the services. UI or Hardware client do not know anything about backend services and there is no direct communication between them at all. We initially developed our application using ASP .Net Core React template, and we got backend and UI in same project. Shortly after adding APIM between those two, we split them to different projects and finally with different repositories with separate release cycles and CI/CD pipelines. Sounds great, doesn’t it?
So far, we did not use APIM to improve performance of our system. It is too early for that. However, I want to mention two promising features of how APIM can help in this space:
APIM help us to structure our application by forcing decoupling of clients and microservices. After presented changes we laid the foundations for a solid architecture that should nicely evolve with the system and adapt to new requirements.
I hope I manage to arouse your curiosity in the topic. When starting working with APIM for the first time, you need to be prepared for some challenges. However early discouragement should quickly pass away and you will begin to appreciate the benefits of this additional layer.
See what we have to offer - Careers