kentoh - Fotolia
4 microservices antipatterns that ruin migration
A migration to microservices can quickly lead to a chaotic heap of overly-coupled modules and fragile code. Here are the most critical microservices antipatterns to avoid.
The primary goals of a migration to microservices architecture are to enhance user experience, gain better control of resource consumption and accelerate the frequency of stable deployment. However, developers may unknowingly pursue certain microservices antipatterns that can completely undermine these benefits.
In this article, we review some of the bad implementation habits that can turn microservices migration upside down. Review these common antipatterns and learn about the ways to both avoid and remedy them.
Distributed monolith antipattern
One of the most common microservices antipatterns is the distributed monolith. To determine if you are building a true microservices architecture or a distributed monolith, ask yourself these two questions.
Do you need to deploy other microservices if you change any microservice?
If the answer is yes, the microservices may not be adequately decoupled from each other. Remember that the goal of a microservices architecture is to independently develop, test, deploy and maintain services.
Examine your deployment process, and then implement strategies to reduce or eliminate service interdependencies. Ideally, all microservices development projects should follow the SOLID principles of software architecture:
- Single responsibility
- Liskov substitution
- Interface segregation
- Dependency inversion
SOLID can mitigate the communication chaos that can occur when there is no functional separation of concerns. Microservices should only communicate with the services it needs to perform its job, rather than connect based on a non-contextual, predetermined order. Many teams have also turned to the API gateway pattern as a method of appropriately segmenting service communication.
Do the microservices share a data store?
In a monolithic architecture, services typically share the same data store. Alternatively, a distributed microservice architecture should provide each service its own data store.
When several microservices share a data store, resource contention can become a major challenge as the services compete for access to data. A shared data store between microservices can also cause deployment problems, because any change to the database schema will affect every service that depends on it.
This antipattern often appears when developers refactor existing monolith code for a microservices architecture. As monolithic applications grow in scale over time, deployments can take more time, performance can degrade and maintenance can become untenable. You might think of an application in such a state as an overloaded piggy bank that needs its contents distributed to handle any additional growth.
To address this problem, refactoring processes should break large blocks of code into small, manageable and decoupled sets. Developers should decipher the service boundaries based on the domains, isolate the services specific to a domain and define the entities properly.
In an entangled data antipattern, all the services have complete access to all database objects. This is also referred to as data taffy, because, like the candy, it's notably hard to pull data apart. This setup makes it hard to scale specific modules and make structural changes to your architecture.
Make sure to isolate relevant data to specified, segregated domains within the application, and ensure that only the services linked to that domain have access to the data within. It's also important to implement policies that control access to the database objects service-by-service.
In a microservices-based application, a poor versioning strategy will make it difficult to manage code over time. When service consumers are tightly bound to a specific version of the service, upgrades and management processes quickly become complicated.
To avoid versioning antipatterns, it's critical to put explicit versioning practices in place. A simple way to do this is to assign specific APIs to certain services. When a new version of that API is created, make sure that the service connected to it versions as well.
Design for change. Architect services working under the assumption that change is inevitable. The goal of consistent versioning is to flexibly manage service changes over time and allow every consuming service to upgrade gracefully.