This article is an introduction to developing microservices-based applications and managing them. It describes architectural design and implementation approaches using .NET Core and Docker containers. This article was written for .NET developers and solution architects who are trying to make a decision about their application architecture and are not familiar with the microservices-based architecture. In this article you will learn about the advantages of implementing microservices-based architecture instead of developing a monolithic application and approaches to solve the main problems that you may meet due to this approach.
Microservices design pattern promotes the development of complex applications as a number of small independent parts (services), that respond to business needs. Each microservice is responsible for a specific function of the system.
Instead of a monolithic application, microservices can be hosted in different places and developers can deliver some important changes in real-time, without stopping the entire system.
Why microservices instead of monolithic architecture?
For a long time, the cost, time, and complexity of provisioning new hardware have unequivocally impacted application development. Since applications were written to be statically sized and designed for specific hardware when load caused an application to outgrow its hardware, the answer was just to ‘scale-up’, or to upgrade the application’s hardware to add capacity, to avoid data center reconfiguration and software refactoring.
The monolithic application model was limited and inefficient. Changing any part of the application might cost a lot of time and make a bug in any place of the entire system. In this case, developers should debug a huge codebase to find out what went wrong. Any newly delivered feature can destroy everything for a time.
Enterprises more and more often trying to resolve some problems, when using big corporate applications to suit their own needs:
- save costs
- become deployment process less painful
- improve DevOps
These problems can be solved by using microservices architecture in parity with containers. Docker containerization is becoming the standard in the application’s deployment. The advantages of containerization are flexible, lightweight, secure, scalable and portable architecture. With the help of Kubernetes, you can easily deploy and host your containers, manage containerized workloads and services.
In this case, when you have a high-loaded application you don’t need to scale the entire application, as it should be done in case of using monolithic architecture. You can scale only a few microservices that have the highest load and improve your performance with minimal resource consumption.
Microservices architecture Pros and Cons
Microservices architecture is an approach to building a server application as a set of small services. Each service should run within its own process and communicate with other services using protocols such as HTTP/HTTPS, WebSockets, etc. Any part of this structure (means service) should be developed as an autonomic unit, which can be deployed independently. Each microservice should own its related domain data model and domain logic and can be based on different data storage technologies. By the way, it can be written in different programming languages.
The main point during developing microservices is to create loosely coupled services, to provide abilities to independent deployment, scaling, and development of each service in the application. If we talk about the size of microservices, we should try to make them as small as we can, but leave them with a minimum of direct dependencies from other microservices. It provides long-term agility, scalability and better maintainability in the future.
Building fine-grained microservices-based architecture enables continuous integration (CI) and continuous delivery (CD) practices. It gives an opportunity to easily integrate new features into applications that are already in production. You can entirely change one of the microservices to implement new functionality or even break this service, and it will not break the entire system that you have built.
Finally, you can add unit tests to every microservice and it will give an opportunity to easily detect any problems or bugs in your system before they will go to production. In this case, you don’t need to check the entire application, you should just debug a small part of code – current microservice.
Successfully created a full-featured microservices application and deploying to the specific platform for managing microservices means that you will have some benefits, like cost efficiency, scalability and 24/7 availability. Keeping microservices healthy can be reached by moving instances to healthy VMs or servers by the platform. So when the software or hardware on which they are running fails or must be restarted for upgrades, the system automatically moves them to the safe place where these microservices can successfully continue to work. By the way, microservices can be scaled in the same way, just by copying service to the new virtual machine and starting one more instance.
Platforms, that can be used to host and manage your microservices:
- Mesosphere DCOS
- Docker Swarm and Docker Compose
- Pivotal Cloud Foundry
- Service Fabric
All of these platforms can be successfully used to run your application on Azure infrastructure without any problems or specific workarounds. Easily managing, scaling, deploying and development of microservices, isn’t a revolution in application development?
Problems and solutions for distributed data management
1. How to define the boundaries of each microservice
The first problem you meet when you try to create microservice architecture is to define microservice boundaries. Each service should be autonomous and stay a piece of the entire system. You should save all the benefits that microservices infrastructure can provide.
You need to focus on the logical domain models and related data. Try to identify the decoupled islands of data and different contexts within the same application. These contexts should be managed and defined separately.
2. How to create queries that retrieve data from several microservices
The problem is that you are trying to avoid direct client referencing to different services. Especially when the client’s application should call different microservices API to perform one task. For example, when you need to get a user role (Security service) and order history (Orders service). Sending different requests to perform one action can be a really bad solution.
The most popular ways to avoid this problem are:
- Creating an API gateway to receive all the client requests and distribute requests on other services. Even if any of your microservice needs to get some data from another microservice, you can send a request to gateway microservice. This request will be redirected to the right receiver.
- CQRS with query/reads tables. This solution consists of aggregating data from multiple microservices using the Materialized View pattern. In this case, you just generate a read-only table with the data that is owned by different microservices. This table has a format that should be received by the client.
3. How to design communication across microservice boundaries
One more problem that you should resolve is communication. A popular approach is to use HTTP-based microservices, which is perfectly acceptable. It is a good idea to use this solution to interact with API Gateway or different microservices. But there is some risk because you can create an excessively long chain of HTTP calls that can convert your microservices-based application to something monolithic and lose all of your benefits.
Why API Gateways better than direct communication with microservices
When you develop an application, you can’t predict anything. You should build an architecture that can be easily scaled, improved or changed. If you have direct requests from clients to your microservices, any update of your microservice response can cause errors. Clients should be updated to use different endpoints or receive a response in different formats. If you use API Gateway, any microservices changes will not touch your clients in any way. You should just change gateway settings to use new endpoints or get a new response. Or even change the new responses to the format that clients used before.
Finally, if you using direct communication, you can meet the following issues:
- Coupling. The client applications should know where and how to get the data they need. It should have direct links to the services that can provide the necessary information.
- Too many round trips. If you need to call different microservices to perform one action, you can get some multiple network round trips and significant latency. It can cause performance issues.
- Security issues. In case of using direct communication, you should open your microservices to the world, to give clients the ability to use your application. In case of using API Gateway, you can make your microservices reachable only from Gateway microservice. It is a more safe way.
- Cross-cutting concerns. Any of your microservices that have direct communications with a client should have some logic to check authorization, get user roles and permissions, etc. It can cause performance issues. In case of using Gateway, this permissions checking should be only in Gateway microservice.
Selecting the right architecture for application is one of the most valuable decisions to create quality, high-performance and optimized solutions. And there are many risk factors you need to consider.
|Data management||You can use one storage for data of entire application||Distributed. You should find a way to isolate your microservices from each other or find a way to sync data between them|
|Development||Everybody in the team should be familiar with the entire application architecture||Each service can be developed independently, by different teams|
|Deployment||You can deploy and scale only the entire application||Each service can be deployed independently. You can scale only the services you need and spend less money|
|Getting data||You can get any data you want in one simple query||You should find ways to collect data from different microservices|
|Costs||You will spend more money in case of scaling your application||Independent hosting of each service can save cost|
The solution depends on the size of your application. The monolithic application architecture can be a good idea for small and lightweight applications. But it is not really good for complex or evolving applications. It is much easier to build monolithic architecture at the start of application development. But in future, in case of evolving, expansion of functionality, the continuation of development you will get more problems and spend more money. If you need to scale your application, in case of monolithic, you should scale the entire huge instance of your app. In case of microservices-based architecture, you can scale just a few microservices and save your costs.
So what to prefer, microservices or monolithic? The answer is quite simple – if you have a small or lightweight application – you can use monolithic architecture to build your system really quick. But if you try to create something big – you should prefer microservices-based architecture.