On August 28th, I attended the .Net Meetup organized by DataArt, where we discussed microservices implemented with ASP.NET Core. And today I wanted to tell you about the characteristics of a microservices architecture. Most of this article is based on the one by Martin Fowler, Microservices, from which we drew some conclusions.
The usual approach is to design monolithic architectures. It’s common for our applications to have a layered architecture, with a single code base and multiple modules. Usually, there is the user interface layer, the business logic layer, and the data access layer. If one of the modules is modified in any way, it’s necessary to deploy the entire application again. So, scalability becomes a challenge, since all the modules must be even for the whole system to scale, leading to a risk of coupling inherent to this architecture. For that reason, it’s very difficult to change the technology, language, or framework, since the application as a whole is deeply coupled and its components are mutually dependent.
Source – Monolithic Architecture
What are Microservices?
A microservices architecture is built upon a set of loosely coupled services, where one service is distributed into others (the microservices) and each one is responsible for doing one thing only.
Source – Microservices Architecture
Unlike monolithic applications, that have to scale as a whole, a microservices architecture allows us to scale each microservice independently. In that way, we can meet the demand or increase the functionality of the area that requires so, without affecting the others. Developing applications with a microservices approach allows us to improve the integration and the continuous delivery of packages, since we can partially deploy one specific service. Similarly, each microservice can be isolated and have its own environment and technology, which are adapted to its specific function.
Components Organized Based on the Business
Components and microservices can be replaced and updated independently. Usually, when a big application is separated into components, companies focus on creating teams with a development scheme similar to the organizational one (UI teams, server-side logic teams, database teams, etc.). That is something called the Conway’s Law. “Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure”, said Melvyn Conway in 1967.
Source – Conway’s Law in Action
Due to their modular architecture, microservices are organized based on the business capacity, where teams are cross-functional and adapt to the requirements needed to develop each functionality separately. That is why there are teams for each specific area (Orders, Shipments, Inventory, etc.) as well as for the associated components, like user interface, storage, and the software stack to be implemented within each microservice.
Today, most of the efforts of applications development are directed towards projects that, once the software is finished and delivered, are transferred to a maintenance team, and the original team is broken up and assigned to other projects. On the contrary, each microservice is considered to be a product on its own, so there is a team that holds ownership of it until its life cycle ends.
Intelligent Services, Simple Communications
The microservices developers community favors a certain approach to the communication structure of the microservices, the Smart Endpoints and Dumb Pipes principle, whose goal is to keep applications as decoupled and consistent as possible. There are two ways to accomplish that:
- Request-response HTTP communication between APIs, following the principles and protocols that resemble the way the Internet works.
- Light messaging using simple implementations, like RabbitMQ or ZeroMQ, where the logic for producing and consuming messages is maintained in the services.
Data Decentralization and Management
While monolithic applications use a single database to ensure information consistency, microservices keep different databases for each service. Those databases can use the same storage technology or a variety of technologies (Cassandra, MongoDB, SQLServer) for different types of data. We refer to this data persistence as Polyglot Persistence. Decentralizing data management through microservices involves updating the database in a non-transactional way and implementing a messages queue or remote procedure calls (RPC).
Source – Data Decentralization
Teams implementing a microservices architecture use automation tools in order to reduce, together with DevOps, the complexity of building, deploying and operating the microservices. Of them, continuous integration (CI) and continuous delivery (CD) make it possible to maintain a scalable system, with automatic tests and detailed monitoring/logging. In microservices, the use of containers such as Docker allows us to deploy our application without worrying about hardware.
Source – Module Deployment Differences
Given that services can fail at any time, applications must be designed to be fault-tolerant. For that reason, application monitoring and logging in real time is paramount, either with a dashboard that enables visualization of the state of each service, or with relevant metrics that help determine if the service is working properly.
In microservices, it is advisable to consider versioning as the last resort. It’s best to design applications as tolerant as possible to changes made by suppliers. Each service has to be able to evolve and scale on its own, without the need to replicate changes in other services. It’s important to bear in mind that a service can vary, be rewritten or even removed. Such changes must not affect other services and the system integration along its life cycle.
Pros & Cons
As my colleague Rodolfo Cordero explains in his article, microservices offer a number of advantages: scalability, flexibility to use different technologies, and the possibility to manage versions better. In addition, they enable partial deployments and distribution of responsibilities. On the other hand, Rodolfo says that they can also cause several problems, depending on the expertise of the team assigned with maintaining the system infrastructure. Besides, our microservice can become a nanoservice, and its operational cost (communications, configuration, debugging) may exceed its utility.
Are Microservices the Future?
It’s hard to know when an application ceases to be just an inventory for selling books from the garage of our home and become the system that supports the biggest online retailer in the world (like Amazon). It’s wise to be prepared for the evolution of our application, and design it in a way that it can support a system with multiple functionalities, where each one can grow on its own and where each component is an individual product.
That doesn’t mean that monolithic applications will be obsolete. These two architectures can coexist, even within the same application. Today, big companies find it difficult to change schemes, designs and architectures that are part of its technology stack and legacy systems. Based on the needs and capabilities of the business, we can identify if our application needs to be distributed in different services and whether these can coexist in a way that the general integrity of the system isn’t affected.
In the next article of this series on microservices, we will review how to implement them with ASP.NET Core and we will discuss the technologies available to build an environment than can support applications development with this type of architecture.