Mastering Software Architecture: Part 6 — Unraveling the Wonders of Microservices Architecture
In the dynamic realm of software architecture, a transformative force has shaped the landscape since 2012 — the advent of microservices. Much like the impact of service-oriented architecture (SOA) in 2006, microservices have redefined our approach to software solutions, offering solutions to complex problems and introducing a new era of scalability and flexibility.
Deciphering the Microservices Paradigm:
The essence of microservices lies in an ecosystem of purpose-built, independently deployed services, accessible through an API gateway. Whether invoked by a user interface, typically a microfrontend, or an external request, client requests traverse well-defined endpoints in an API gateway. This gateway then relays requests to individual services, each managing its data or interacting with other services through a precisely defined API.
Each microservice claims ownership of its set of tables, usually arranged in a schema within a highly available database or a domain-specific database. This ownership model ensures that only the respective service can access and update the associated data. If other services require access, they must request information from the owning microservice rather than directly accessing the tables.
The API gateway serves the crucial role of concealing the location and implementation details of services tied to API gateway endpoints. Diverging from the enterprise service bus (ESB) in service-oriented architecture, the microservices’ API gateway abstains from housing business logic, orchestration, or mediation, upholding the sacred principle of a “bounded context.”
Microservices, embodying their name, epitomize single-purpose, independently deployed software units designed to excel at specific tasks. The term “micro” transcends physical size, emphasizing functionality. Microservices, characterized by their fine-grained and single-purpose nature, can lead to the deployment of hundreds or even thousands of services within a given ecosystem or application context. They can be deployed as containerized services, such as Docker, or as serverless functions.
Navigating the Bounded Context:
In the microservices paradigm, each service usually claims ownership of its data, encapsulated within the concept of a “bounded context.” This context ensures that all source code, data structures, and data related to a specific domain or subdomain are encapsulated as one unit. The bounded context is pivotal for managing change within a microservices ecosystem, offering not just architectural agility but also meticulous control over change.
This concept is known as a bounded context, a term coined by Eric Evans in his book Domain-Driven Design . Within the scope of microservices, this means that all of the source code representing some domain or subdomain (such as a wish list for a customer), along with the corresponding data structures and data, are all encapsulated as one unit.This concept is critical for microservices. As a matter of fact, microservices as an architecture style wouldn’t exist without the notion of a bounded context. To illustrate this point, imagine 250 microservices all accessing the same set of tables in a monolithic database. Suppose you make a structural change (such as dropping a column or table) that 120 of those services access. This change would require the coordination of modifying, testing, and deploying 120 separate services at the same time, along with the database change. This is a scenario that is simply not feasible. Within microservices, the bounded context not only facilitates architectural agility (the ability to respond quickly to change), but also manages change control within a microservices ecosystem. With the bounded context, only the service that owns the data needs to change when structural data changes happen
Distinctive Features that Define Microservices:
Microservices stand out owing to three defining features: distributed data, operational automation, and organizational change.
Distributed Data: Microservices necessitate the distribution of data across separate services, driven by the multitude of services typically present in a microservices architecture. This distinguishes microservices from other architectures that can function with a single monolithic database.
Operational Automation: The sheer number of microservices mandates operational automation for testing, deployment, and monitoring. Containerization, service orchestration (e.g., Kubernetes), and DevOps become imperative to manage the parallel development, testing, and release of numerous services.
Organizational Change: Microservices demand a unique organizational structure, with development teams organized into cross-functional units specializing in user interface, backend, and database development. Each team owns specific services, along with testing and release responsibilities. This organizational change is crucial for effectively managing the large number of services in a microservices ecosystem.
Applications that seamlessly align with the microservices architecture include those with separate and distinct functions within a business workflow. Consider a retail-based order entry system where various functions, such as order placement, payment processing, inventory management, customer notification, and more, operate as independently deployed microservices.
Another compelling use case unfolds in business intelligence and analytics reporting. Each report, query, data feed, or analytics function can be developed as a separate microservice, tapping into data within a data lake or data warehouse.
Analyzing Microservices Challenges:
While the benefits of microservices are profound, they come with considerable challenges, making them one of the most intricate architecture styles.
Service Granularity: Determining the appropriate granularity of services, guided by the single responsibility principle, can be subjective. Factors like code volatility, fault tolerance, scalability, throughput, and access control offer more objective criteria for justifying service granularity.
Communication Between Services: Microservices architecture introduces the challenge of choosing between asynchronous and synchronous communication. Deciding whether to use orchestration or choreography for workflows further complicates communication choices.
Data Management: Microservices present challenges in managing data, especially regarding inter-service communication. Decisions on how services should access data, such as via REST, caching, schema expansion, or data sharing, involve trade-offs.
When to Embrace Microservices Mastery:
Consider embracing the microservices architecture when:
Your application functionality can be feasibly broken into numerous independent pieces.
High levels of agility, fault tolerance, and scalability are required.
Extensibility is a key consideration for future architectural enhancements.
When to Tread Cautiously:
Microservices might not be suitable when:
Complex workflows and extensive inter-service communication or orchestration are required.
Data is tightly coupled and challenging to break into separate schemas or databases.
High-performance or highly responsive systems are essential due to potential communication latencies.
Fault Tolerance: ⭐⭐⭐⭐⭐
Embracing microservices is a strategic decision that demands careful evaluation based on your application’s unique characteristics, business requirements, and scalability considerations. While complex, when implemented correctly, microservices can unlock unprecedented flexibility and scalability in modern software development.
Stay tuned for the next chapter in our “Mastering Software Architecture” series, where we will delve into the intricacies of another cutting-edge architecture style!