Cloud Design Pattern Tuesdays

May 7, 20251064 words6 min read
azure
waf
design patterns
architecture

WAF Design Patterns

This collection contains a summary and simple implementation of the cloud design patterns covered by the WAF.

Ambassador Pattern

The Ambassador pattern is a design pattern where a helper service acts as an intermediary between a client and backend services.

It's particularly useful for:

  • Offloading responsibilities like logging, routing, retry policies, or monitoring
  • Supporting legacy applications or hard-to-change systems
  • Building shared client connectivity features
  • Offloading connectivity concerns to specialists

This pattern is best suited when you need to abstract cross-cutting concerns and is commonly used in microservices architectures. However, it's not recommended for latency-critical applications or when features require deep client integration.

Read more about the Ambassador Pattern

Anti-Corruption Layer Pattern

The Anti-Corruption Layer pattern introduces a layer between two subsystems to prevent undesirable dependencies and preserve the integrity of the internal model.

It's particularly useful for:

  • Integrating legacy systems with modern applications
  • Working with external services that have different models or protocols
  • Ensuring an application's design isn't limited by external dependencies
  • Maintaining clean boundaries between different subsystems

This pattern is best suited when you need to integrate systems with differing semantics, but it's not recommended when there are no significant semantic differences between the systems or when the layer would introduce unnecessary complexity.

Read more about the Anti-Corruption Layer Pattern

Asynchronous Request-Reply Pattern

The Asynchronous Request-Reply pattern decouples backend processing from frontend hosts by allowing the sender to continue processing without waiting for an immediate reply.

It's particularly useful for:

  • Decoupling services and increasing system reliability
  • Handling long-running operations without blocking
  • Buffering workloads and managing system load
  • Enabling systems to operate independently

This pattern is best suited for distributed systems and event-driven architectures where immediate feedback isn't critical. However, it's not recommended for real-time systems requiring low latency or for simple operations where the added complexity isn't justified.

Read more about the Asynchronous Request-Reply Pattern

Backends for Frontends Pattern

The Backends for Frontends Pattern creates dedicated backend services for each frontend interface (web, mobile, IoT) to handle their unique requirements and optimize their specific needs.

It's particularly useful for:

  • Applications with multiple frontends requiring different data shapes
  • Reducing over-fetching and under-fetching of data
  • Improving modularity and maintainability
  • Simplifying API development for each frontend type

This pattern is best suited when frontends have distinct requirements that would make a shared backend overly complex. However, it's not recommended for applications with simple or uniform frontend requirements where a single API would suffice.

Read more about the Backends for Frontends Pattern

Bulkhead Pattern

The Bulkhead Pattern isolates elements of an application into pools to prevent cascading failures and ensure that a failure in one component doesn't impact others.

It's particularly useful for:

  • Preventing cascading failures across microservices
  • Isolating critical resources like database connections
  • Managing task queues and compute resources
  • Improving fault tolerance and recovery strategies

This pattern is best suited for complex applications where component isolation is crucial for reliability. However, it's not recommended for lightweight applications where the isolation complexity would outweigh the benefits, or for services with minimal risk of resource exhaustion.

Read more about the Bulkhead Pattern

Cache-Aside Pattern

The Cache-Aside Pattern is a caching strategy where the application is responsible for managing both reading from and writing to the cache. Data is loaded into the cache on-demand.

It's particularly useful for:

  • Read-heavy applications that can tolerate minor data staleness
  • Reducing load on the primary data store
  • Improving application performance and scalability
  • Applications where you need granular control over the cache

This pattern is best suited for scenarios where read performance is a priority and eventual consistency is acceptable. However, it is not ideal for write-heavy workloads or when strong data consistency between the cache and the data store is required.

Read more about the Cache-Aside Pattern

Choreography Pattern

The Choreography Pattern is a decentralized approach to service orchestration where each service reacts to events and independently coordinates its part in a workflow.

It's particularly useful for:

  • Event-driven architectures and microservices
  • Systems that benefit from loose coupling and service autonomy
  • Improving reliability and scalability by removing central points of failure
  • Workflows that evolve independently across services

This pattern is best suited when you want services to operate independently and communicate through published events. However, it is not ideal for highly structured workflows that require strict ordering, centralized control, or complex error recovery logic.

Read more about the Choreography Pattern

Circuit Breaker Pattern

The Circuit Breaker Pattern is a stability pattern that prevents an application from repeatedly trying operations likely to fail by temporarily halting calls to a service when failures cross a threshold.

It's particularly useful for:

  • External service calls and remote APIs that may experience intermittent issues
  • Preventing cascading failures and isolating faults
  • Improving system resilience and availability
  • Enabling fallback paths and graceful recovery

This pattern is best suited when you want to safeguard systems against persistent failures and avoid overwhelming dependent services. However, it may add unnecessary complexity if failures are rare or short-lived, or if retrying has minimal impact.

Read more about the Circuit Breaker Pattern

Claim-Check Pattern

The Claim-Check Pattern offloads large or sensitive data payloads to external storage and transmits only a reference (claim check) through the message pipeline.

It's particularly useful for:

  • Messaging systems with strict size limits
  • Reducing message size and optimizing bandwidth
  • Exchanging large or sensitive data without sending it directly through the messaging infrastructure
  • Improving performance, scalability, and security

This pattern is best suited when large objects or sensitive data need to be exchanged but shouldn't travel through messaging infrastructure directly. However, it adds operational complexity and potential latency, so it's not recommended for simple or lightweight messages.

Read more about the Claim-Check Pattern

© 2025 Andrei Bodea