With scalability and less time-to-market being the topmost concerns of the businesses in today’s competitive market, microservices patterns have become one of the most discussed topics in the technology domain. However, there’s a fear that cripples organizations and developers when it comes to adapting microservices architecture – it might not be a feasible idea for all and could lead to failure.
There are many legal, cultural, and technological constraints that could limit the use of microservices in an organization. Some organizations in highly regulated industries have different security/compliance needs as compared to Spotify or Netflix (as these use microservices).
Instead of fully adopting a microservices patterns architecture, one can consider microservices as a series of overlapping patterns from where you can pick and choose the most suitable option as per your organization’s needs.
Microservices patterns can be adapted pragmatically, in a manner that makes sense for your organizational requirements and fits into the cultural, ethical, legal, and technological space easily. There is no need to adopt every aspect of microservices simultaneously.
What are Microservices?
Microservices or Microservices patterns were initially created as an alternative to monolithic architecture. However, with time, it has been concluded that microservices patterns architecture is a group of related patterns that share the same set of goals.
For example, RDBMses, NoSQL Stores, Time Series Databases, and Big Data Stores all share a similarity – they can store and query data. However, their specifics are different.
There are 6 major microservices patterns with different specifics and priorities. They cannot be compared with one another in best or worst scenarios, as it completely depends on your requirements.
They all follow the microservices architecture goals, including speed, scalability, and cohesion, but arrive at them differently. Each of them follows specific patterns, and you can choose to make a set of trade-offs that prioritize particular things over others.
The intended goal of this blog is to help you design and implement a microservices patterns architecture using a blend of these patterns rather than picking one. No single pattern is the best in general. However, each pattern is better at a particular task as compared to others, which will give you space and ease to select the one that you want.
Fine-Grained SOA
Fine-grained SOA is arguably a dominant pattern of microservices patterns architecture. For many, Netflix is the origin of fine-grained SOA, and this was their articulation of the pattern at the beginning. It is a self-explanatory concept – reduces the issues with SOA, divides the design further into smaller, fine, grain-like segments, and works on the same principle as the SOA.
Despite getting more control and individuality in the design, it was noticed that Netflix and others who chose to work with fine-grained SOA experienced several basic issues. When the design was divided into smaller parts, in an attempt to scale them, some difficulties emerged.
For example, with one design you used to make one call, with hundreds of smaller designs, now you have to make hundreds of calls, which is tedious and inefficient — managing one thing V/s. managing hundreds of little things requires more effort, time, and tech support. If you are thinking of adapting fine-grained SOA, make sure that you have your management tools ready in advance.
Some of the overlooked challenges with fine-grained SOA include inefficient inter-process communications, management, governance, overarching monitoring, and consistent state. When you plan to implement fine-grained SOA, know what you’re getting into and how to tackle it because these pain points will bother you at some point in time.
The automation of integration, testing, and deployment becomes a critical task. However, the ability to change is rapidly improved. Also, with an automated infrastructure in place, things become quicker, easier, and better.
Usecase
Fine-grained SOA can be used when you are planning to work with a massive complex system having minute details that need to be managed and controlled efficiently. However, as discussed above, since the complex system is divided into numerous individual units, one needs a better, faster, and more efficient management system that can control and manage the individual units easily.
Layered APIs over Fine-Grained SOA
This approach can be thought of as a layered version of the API design. Layered APIs are well-aligned with fine-grained SOA and, often, these two can co-exist. Fine-grained, layered APIs can become an evolutionary pattern that follows fine-grained SOA.
This approach defines a structure for a fine-grained SOA approach. Often, standards are put in place that brings together microservices patterns that share a similar purpose, which rationalizes complexity. The ability to change is also improved rapidly through standardization and further decomposition of the design.
Nonetheless, this approach also has a few drawbacks which are similar to fine-grained SOA. In layered APIs, one has to make multiple calls through the defined layers which are inefficient from the perspective of “network hops.” The ultimate goal of layered APIs is to render increased flexibility while giving a structure to the architecture in a way that can separate concerns as well.
With layered APIs, you can manage a large number of layers with a huge number of fine-grained units, which calls for a powerful management system that visualizes complex microservices. The end-application datastore consistency is improved because the set of things that modify or query these units are more organized and focused, for example, System APIs.
In a nutshell, the layered API approach to design is a reliable and good option to consider. Though you might face some difficulties on the way, layered APIs mostly create issues at a large scale.
Usecase
If you have a system where some of the units share a common purpose, you can opt for layered APIs over fine-grained SOA. While fine-grained SOA creates numerous, finite units from a huge system, layered APIs create individual layers that are grouped based on their purpose, for example, systems, processes, domain models, and experience.
Layered APIs give more flexibility to manage and control the entire design by giving a proper structure to the architecture. To use layered APIs to its fullest potential, one needs to create a definite way to structure an architecture. Microservices patterns architectures without some amount of structure are difficult to reason and rationalize with, which makes it difficult to visualize and categorize things based on their purpose.
Message Oriented State Management over Layered APIs
It is usually the first pattern to be implemented as a way to avoid the side effects of accessing and mutating the state. It provides an asynchronous queue as the primary mechanism to communicate State changes (by event or command) or to query the microservices, which allows each Microservice the time necessary to converge events and therefore provides a consistent external view.
In simple words, when a change in data occurs, it is sent in the form of a message or a queue or ESB (Enterprise Service Bus) to any other Microservice or store that needs to be notified about the change.
This pattern is used by teams who are well versed with SOA and ESB, as this is an intuitive path to follow given their prior experience. This pattern is mostly recommended for organizations who are just starting on their microservices patterns journey as it is beneficial in the initial stage and can very well act as a transitory stage. However, the problem arises when the system becomes more complex.
With this pattern, we decouple components temporarily using a queue, the implementation and behavior of each Microservice then become obscured, which might result in more noticeable side effects which could have been avoided otherwise.
Usecase
Supposedly you have a system where if the signaling network indicates a “Yes,” you want the user to receive a pop-up notification informing a “Success” message. Contrarily, if the signaling network indicates a “no,” you want the user to get a “Failed” message. A Message-oriented State management pattern can be utilized in such cases, but only as a transitory phase.
One significant drawback of message-oriented state management is that you need to replicate the state of key business data between microservices patterns or data stores to ensure data integrity.
Event-Driven State Management over Layered APIs
Event-driven systems provide more consistency, efficiency, and improved performance. When overlaid on microservices patterns, they provide some powerful abstractions which can be a game-changer for the entire design. Event-driven systems usually use a defined queue (just like message-oriented systems), they enforce a standard around the design and behavior of what is passed over the queue.
An event can be defined as something that occurred in the past with an associated representative state and timestamp. This event enables the microservice receiving it to reconstruct or replicate a materialized view of the state by replaying the same events in the proper sequence.
However, in many scenarios, this concept is confused, where events (something that has already happened) are mixed with commands (make something happen in the future), and without the distinction, the design’s predictability becomes flawed. Despite this, the pattern is significantly better than message orientation. However, it tends to show some problems in the future due to a lack of consistency.
It also doesn’t offer any specific options on how to deal with data conflicts or to reconstruct states in case of any damage or failures. For those who wish to use this pattern, it is advisable to articulate and enforce some set standards which will reduce the inconsistency and provide many stable results.
Usecase
Event-driven State management systems are quite similar to message-oriented state management systems. The architecture is very easy to work with and understand because of its standardized nature. One can use a common event abstraction to represent the unit of change in the architecture.
Similar to message-oriented state management, event-driven state management also requires replicating key business events to synchronize between microservices or data stores to ensure data integrity.
Isolating State in Layered APIs
The isolating state typically means that each microservice patterns contain its state. In this pattern, each Microservice pattern contains an internal data store that it constantly reconciles with external stores so that it becomes the only source of truth.
The process mentioned above is difficult as a single source of truth patterns tends to echo the complexity of the master data management and its linked challenges. However, having an external store data which serves as a single source of truth is quite practical, given each Microservice pattern works towards a common purpose.
For example, it might be difficult to isolate the state of a customer, but one can always isolate a customer’s phone number or email address. Isolated pattern favors extremely granular microservices, focusing on the minute details. It requires an asynchronous event propagation as a means to pass state change from one point to another.
Microservices contain a data store that serves as a single source of truth. Supposedly, there is a “product,” now every information associated with this product could be stored in its Microservice which contains a MySQL database and serve as the only way to query or update the concept in the organization.
Usecase
You can use this pattern in instances where “reuse” is not a necessity. Every Microservice patterns have a “use.” Unlike the SOA pattern, “reuse” doesn’t tend to be a priority in these designs. If however, reuse occurs, it is due to accidental causes and not intended. This pattern requires some governance to ensure that the data is unique and can not be accessed from elsewhere.
This is fairly easy to understand and implement a highly scalable pattern. Speed of change is also good due to more cohesive architecture but requires governance to ensure the architecture is not breached.
Having multiple benchmarks or standards set in a single architecture often creates problems in decision making. It is important to have one single source of truth as a reference for a particular business unit. When it comes to isolating states in layered APIs, one can easily nominate Microservice patterns which can serve as a single source of truth for a given business entity.
You can also encapsulate the state inside the microservice. Otherwise, data integrity becomes a major issue if there are multiple sources of truth.
Replicating State in Layered APIs (Event sourcing)
Replicating state is essentially a solution to the problems posed by isolating state pattern; specifically where consistency is required. For example, consider a catalog for pricing and currency microservice, each of which contains an isolated state of each thing. This makes them interdependent, any failure or change in one of these can cause the function of others to fail.
This issue can be addressed using replicating state which provides a single space to store all state mutations which can be used later to rebuild internal states by each isolated microservice. Often, this is coexistent with event sourcing, which provides a separate single source of truth that is consistent and where event-driven microservices patterns communicate exclusively through an immutable event log.
This design by nature is eventually consistent. For example, one might think of a debit to a bank account as inherently transactional, however, most of the modern banks have concluded that it’s relatively easier to create a final consistent debit than expend efforts on ensuring every single transaction is consistent. This method enables greater flexibility and improved speed of change, faster time to value in the case of IT systems.
Replicating the state is quite challenging as it requires more effort and a deeper understanding of the state is managed. One should be able to predict the behavior of each microservice pattern to ensure a successful journey. Replicating state also addresses the problems caused by other patterns, it improves cohesion over top-down design, consistency, and speed of change over predictability.
Usecase
The isolating state often creates consistency problems, this is where the replicating state comes into action. You can maintain a single source of truth of all changes to data, and replicate it as and when needed. Replicating the state creates a cohesive architecture that is extremely scalable. Speed of change is also excellent and is easy to work with the pattern.
Similar to isolating state patterns, data integrity becomes an issue when there are multiple sources of truth in this pattern as well. Also, logical dependencies are sometimes difficult to understand and visualize.
Conclusion
Now that you have a fair idea of different microservice pattern architectures, it is very important that you assess which ones would work best for your organization and would fall into your legal, cultural, and technological aspects. You could choose one, or maybe a set, or just stick to your current monolithic architecture.
Remember that these architectures are not a one-stop solution for all your problems, rather they assist in overcoming obstacles that you might face during the journey. It is yet to be analyzed and seen how these patterns evolve in the future, and how software may rise to simplify or reduce the challenges in highly complex approaches.
While many other organizations plan to just hop on to the microservice patterns architecture bandwagon, you should consider all aspects surrounding it and how it could impact your brand. Choose wisely and ensure that you are able to use the specific microservice patterns to their fullest potential.
Read More Blogs:
An Expert’s Guide to Orchestrating Microservices on AWS
Understanding Software Architecture Frameworks – Microservices, Monoliths, SOA, and APIs