Cloud Application: Architectural Styles — Part 3 (Web — Queue — Worker)
Introduction
In this series of articles, we are going to study the cloud application architectural styles, patterns and practical examples. In Part-3 of the series, we are going to have an overview of the “Web-Queue-Worker-driven” architecture style along with its logical diagram, where to use this style, benefits and challenges.
This article also revisits some of the “Microservices” concepts.
We will study further cloud architectural styles and patterns in subsequent articles of this series.
You can check the discussion about “N-tier”, “Microservices” architectural styles in “Cloud Application: Architectural Styles — Part 1 (N-Tier)”
and “Microservices” in in “Cloud Application: Architectural Styles — Part 2 (Microservices)”.
Background
The important components of this architecture include a web front end which serves http requests from client and a worker which runs long running / resource intensive tasks or batch processes. The communication between web front end and worker happens through a message queue.
Web-Queue-Worker architecture style
It can be considered for purely PaaS solutions. The architecture is easy to understand just like N-tier. Worker is an optional component and can be omitted if there are no long running tasks.
Logical Diagram:
There are some other important components also in the microservice architecture:
- Identity provider for authentication
- CDN for serving static content (images, script files, CSS etc)
- Remote services (e.g. SMS or email)
- One or more databases
- Cache for accessing values faster from database
Management:
Responsibilities of the management component:
- Placing services on nodes
- Rebalancing services across nodes
- Identifying failures
Service Discovery
Responsibilities:
- Enable service lookup to find endpoint of the service
- Maintain a list of services and nodes on which they are located
API Gateway:
Responsibilities:
- Acts as an entry point for the clients
- Clients call the API Gateway instead of calling the service which forwards call to the appropriate services on the back end.
- It may aggregate responses from multiple services and return the aggregated response.
Advantages of API Gateway:
- Services can be versioned and refactored without updating all the clients. It decouples the clients from services.
- Services can use non web friendly messaging protocols like AMQP.
- API Gateway can perform other functions like authentication, logging, load balancing, SSL termination etc.
Where to use:
- Large applications which need high release velocity
- Complex apps which are required to be highly scalable
- Apps with rich domains and many subdomains
- An organization having small development teams
Benefits:
Independent development: Each service can be built by a small, focused development team which results in continuous innovation and faster release cycle.
Mixed technology stacks: We can pick the technology stack that is most suitable for the service.
Small, focused teams: The smaller scope of services makes the code easier to understand and easier to ramp up for new developers. The team can focus on one service.
Independent deployments: Service can be updated or rolled back without redeploying the entire application. Fixing defects and releasing features becomes more manageable and involves less risk. Frequent updates can be deployed as we can deploy individual services with minimal coordination between teams.
Granular Scaling: Services can be scaled independently.
Fault Isolation: Even if a service goes down, it won’t stop entire application. The application can become resilient provided you follow resiliency best practices and design patterns.
Challenges:
Data integrity: Since each microservice is responsible for its own data persistence, data consistency can be a challenge. Hence, we need to adopt eventual consistency wherever possible.
(Eventual consistency: using an event-driven, eventually consistent approach, each service publishes an event whenever it updates its data. Other services subscribe to events. When an event is received, a service updates its data.)
With each microservice responsible for its own data persistence. As a result, data consistency can be a challenge. Embrace eventual consistency where possible.
Complexity: Though each service is simple, the system as a whole becomes more complex since microservices application has more moving parts as compared to the monolithic application.
Management: Microservices requires a mature DevOps culture. Correlated logging across services can be challenging. Typically, logging must correlate multiple service calls for a single user operation.
Development and test: Testing services dependencies poses a challenge. Existing tools are not necessarily designed to work with service dependencies. Refactoring across service boundaries can be difficult.
Network congestion and latency: We need to carefully design the APIs, avoiding overly chatty ones and also think about serialization formats and look for places to use asynchronous communication patterns. Using many, small granular services can cause more interservice communication. Also, if the chain of service dependencies gets too long (service A calls B, which calls C…), the additional latency can become a problem.
Lack of governance: The decentralized approach to building microservices has its own Pros and Cons. You may end up with many different languages and frameworks that the system becomes hard to maintain. It may be useful to put some project-wide standards in place without restricting team’s flexibility.
Skillset: We need to carefully evaluate if the team has skills and experience to be successful building microservices since they are highly distributed systems.
Versioning: Updates to a service must not break services that depend on it. Multiple services could be updated at any given time, so without careful design, you might have problems with backward or forward compatibility.
Best Practices:
Model services around the business domain.
Create a Separate Data Store for Each Microservice:
You end up with the situation where if one team updates a database structure, other services that also use that structure has to be changed too.
(Breaking apart the data can make data management more complicated, because the separate storage systems can more easily get out sync or become inconsistent, and foreign keys can change unexpectedly. You need to add a tool that performs master data management (MDM) by operating in the background to find and fix inconsistencies.)
Keep Code at a Similar Level of Maturity:
Keep all code in a microservice at a similar level of maturity and stability. In other words, if you need to add or rewrite some of the code in a deployed microservice that’s working well, the best approach is usually to create a new microservice for the new or changed code, leaving the existing microservice in place
(Immutable infrastructure principle)
Deploy in Containers:
Deploying microservices in containers is important because it means you just need just one tool to deploy everything.
APIs should model the domain, not the internal implementation of the service:
Services communicate through well-designed APIs. Avoid leaking implementation details.
Loose coupling and high functional cohesion:
Functions that are likely to change together should be packaged and deployed together. If they reside in separate services, those services end up being tightly coupled, because a change in one service will require updating the other service. Overly chatty communication between two services may be a symptom of tight coupling and low cohesion.
Isolate failures:
Use resiliency strategies to prevent failures within a service from cascading.
Offload cross-cutting concerns, such as authentication and SSL termination, to the gateway.
Keep domain knowledge out of the gateway:
The gateway should handle and route client requests without any knowledge of the business rules or domain logic. Otherwise, the gateway becomes a dependency and can cause coupling between services.
References:
https://docs.microsoft.com/en-us/azure/
https://martinfowler.com/articles/microservices.html
https://www.nginx.com/blog/microservices-at-netflix-architectural-best-practices/