Skip to content

Cloud Design Pattern Tuesdays

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

Compensation Transaction Pattern

The Compensation Transaction Pattern ensures system consistency by undoing work after failures, using defined compensation or rollback actions to revert the system to a stable state.

It's particularly useful for:

  • Long-running, multi-step workflows across distributed services
  • Scenarios where traditional ACID transactions aren't feasible
  • Improving resilience and consistency in distributed systems
  • Minimizing the impact of failures by isolating compensation logic per step

This pattern is best suited when operations can't be committed atomically and need a way to roll back only the affected parts of a process. However, it's not recommended for scenarios requiring strong consistency and atomicity, or where compensating logic is complex or risky to implement.

Read more about the Compensation Transaction Pattern

Competing Consumers Pattern

The Competing Consumers Pattern allows multiple independent consumer instances to pull messages from a shared queue, enabling parallel processing and improved throughput.

It's particularly useful for:

  • Message-based systems like order processing and telemetry ingestion
  • Distributing load across multiple workers for better scalability
  • Improving system resilience by removing single processing bottlenecks
  • Enabling horizontal scaling to handle varying workloads

This pattern is best suited when workloads increase and processing needs to scale out. However, it's not recommended for scenarios requiring strict message ordering or transactional consistency across multiple consumers.

Read more about the Competing Consumers Pattern

Compute Resource Consolidation Pattern

The Compute Resource Consolidation Pattern consolidates multiple tasks or operations into a single computational unit to maximize resource utilization and efficiency.

It's particularly useful for:

  • Multi-tenant systems and microservices that can share compute capacity safely
  • Reducing the number of underutilized compute instances for cost optimization
  • Simplifying resource management and lowering operational overhead
  • Lightweight, bursty, or predictable workloads that don't require dedicated resources

This pattern is best suited when workloads are lightweight and can run safely on shared compute resources without affecting each other. However, it's not recommended for scenarios requiring strong workload isolation for compliance or security, or where predictable performance is critical.

Read more about the Compute Resource Consolidation Pattern

Command Query Responsibility Segregation (CQRS) Pattern

The CQRS Pattern separates the models for handling commands (writes that change state) and queries (reads that fetch data), allowing each to evolve independently.

It's particularly useful for:

  • Systems where reads and writes have differing workload profiles
  • High read-load applications and event-driven designs
  • Optimizing query performance without compromising write integrity
  • Allowing each model to scale and be maintained independently

This pattern is best suited when read and write workloads diverge in complexity or scale. However, it's not recommended for simple CRUD applications where separation adds unnecessary complexity, or when immediate consistency is critical across data operations.

Read more about the CQRS Pattern

Deployment Stamps Pattern

The Deployment Stamps Pattern involves provisioning multiple independent copies ("stamps") of a solution, each hosting a subset of tenants or workloads, enabling linear scale-out, regional deployment, and data separation.

It's particularly useful for:

  • Multitenant or SaaS workloads needing horizontal scale and tenant isolation
  • Deploying in multiple regions for compliance and performance
  • Staggered updates using deployment rings
  • Solutions that hit scale limits or incur non-linear costs

This pattern is best suited when single deployments hit scale limits or must separate customers by size, update cadence, or region. However, it's not recommended for simple solutions that scale easily on one instance, or systems needing global data replication across all deployments.

Read more about the Deployment Stamps Pattern

© 2026 Andrei Bodea

Privacy policy

Privacy Policy

Last Updated: 2026-08-11

Thank you for visiting Compiled Thoughts (the “Blog”). We value your privacy and want to clarify how we handle any information you may provide or that may be collected when you visit this Blog. By accessing or using the Blog, you agree to the terms of this Privacy Policy.

1. Information We Do Not Collect

  • No Analytics or Tracking — We do not run analytics software, tracking pixels, session recording, or any other tool intended to profile visitors or follow them across sites.

  • No Advertisements — Our Blog does not display third-party advertisements and, therefore, does not collect data for advertising or marketing purposes.

  • No Reader Accounts or Subscriptions — Readers cannot create an account or subscribe, so we do not collect or store reader account data. Sign-in exists only for the Blog’s own author, in order to publish content.

  • No Cookies Set By Us — We do not set cookies of our own for any purpose. Please see Section 2 for the third-party services your browser contacts when you visit.

2. Third-Party Services and Automatic Data Collection

The Blog is a static site with no database of readers. However, displaying a page does require your browser to make requests to the services listed below. Those services necessarily receive your IP address, your browser’s user agent, and the address of the page you requested. This happens automatically when you visit, and we ask that you take it into account when reading Section 1.

  • Web Hosting, Netlify — The Blog is hosted by Netlify, which serves every page and may retain standard server logs, including IP addresses, for operational and security purposes.

  • Typefaces, Google Fonts — Every page loads fonts from fonts.googleapis.com and fonts.gstatic.com. As a result, Google receives your IP address and user agent when you visit. We do not use Google Analytics or any other Google advertising or measurement product.

  • Netlify Identity — The home page loads a login widget from identity.netlify.com. It exists solely so the author can sign in to publish, it offers nothing to ordinary readers, and it is not used to identify you. Your browser requests it regardless, and the widget may use your browser’s local storage to hold a sign-in session.

We do not control what these providers log or how long they retain it. Please consult their own privacy policies if that matters to you.

3. Voluntary Information

If you choose to contact us directly (for example, through an email link or contact form, if provided), we may receive personal information such as your name or email address. In such cases:

  • We will use this information solely to respond to your inquiry.

4. No Third-Party Data Sharing

Beyond the automatic requests described in Section 2, we do not share, sell, rent, or otherwise disclose personal information to third parties, because we do not collect or store any personal information about readers. In the event you voluntarily submit personal data (e.g., via direct email), we do not disclose that to any external entity.

5. Children’s Privacy

Our Blog does not target or direct content specifically to children under the age of 13. We do not knowingly collect or maintain personal information from children under 13. If you believe we may have inadvertently received personal information from a child under 13, please contact us immediately so we can delete such information.

6. External Links

Our Blog may contain links to external websites about programming, interviewing, music, books, psychology, or other related content. We are not responsible for the content, privacy policies, or practices of any third-party sites. We encourage you to review the privacy policies of those websites before interacting with them or providing any personal information.

7. Security

Although we do not collect or store personal data on our servers, we still endeavor to use reasonable security measures to protect the Blog’s integrity. However, no data transmission or storage system can be guaranteed to be 100% secure. Your use of the Blog indicates you understand and accept any inherent risks.

8. Changes to This Privacy Policy

We may update or modify this Privacy Policy from time to time to reflect changes in our practices or for other operational, legal, or regulatory reasons. If we make any material changes, we will update the “Last Updated” date at the top of this document. Your continued use of the Blog after any changes signifies your acceptance of the revised Privacy Policy.

9. Contact Us

If you have any questions or concerns about this Privacy Policy, please reach out using the email address in the header of the page.

By using Compiled Thoughts, you acknowledge that you have read, understood, and agree to this Privacy Policy.