Pattern catalogue
This demo’s write-up is backed by real entries in Architecture Governance Platform - reference architectures, a principle, and the external pattern glossary they’re cross-linked to. That platform normally requires a login, so this page is a public copy of the parts that describe this demo specifically: condensed from the full artifacts, not a live mirror. The glossary below is reproduced in full, since it’s already public, vendor-documented reference material. (For plain-English definitions of the terms themselves - Saga, Circuit Breaker and the rest - see the glossary page instead; this page is the Azure vendor catalogue specifically.)
From Architecture Governance Platform
Reference architecture: Barebones microservices (API Gateway + database-per-service + pub/sub)
Demonstrates three recurring integration/decomposition patterns together, as a small runnable system rather than a diagram alone: a single entry point in front of independently-owned services, each with its own data store, coordinating through events rather than direct calls. Use this shape when services genuinely need to evolve, deploy, and scale on separate schedules, and when “eventually consistent” is an acceptable answer to “when does inventory reflect this order”. Don’t reach for it when a single service with one database would do the job - the coordination and eventual-consistency cost is real and only pays off once there’s a genuine reason to split.
flowchart LR
client["Browser (UI)"]
gw["API Gateway (nginx)"]
orders["orders-service (FastAPI)"]
inventory["inventory-service (FastAPI)"]
broker[("Redis - order_created topic")]
ordersDb[("orders.db (SQLite)")]
inventoryDb[("inventory.db (SQLite)")]
client --> gw
gw -->|"/orders, /inventory"| orders
gw -->|"/inventory"| inventory
orders -->|reads/writes| ordersDb
inventory -->|reads/writes| inventoryDb
orders -->|"publish: order_created + trace context"| broker
broker -->|subscribe| inventory
classDef container fill:#1168bd,stroke:#0b4884,color:#fff
class gw,orders,inventory,broker,ordersDb,inventoryDb container
Two of its three components are already named, vendor-documented entries in the glossary below: API Gateway is a concrete instance of Gateway Routing (route requests to multiple services behind one endpoint); the order/inventory handoff is Publisher-Subscriber. Database-per-service has no equivalent in Microsoft’s glossary - it’s a general microservices decomposition pattern, not a cloud-vendor one.
Reference architecture: Trace context propagation across an event-driven boundary
Any system using asynchronous messaging (pub/sub, a queue, an event stream) between a producer and a consumer loses the connection between them in distributed tracing by default - auto-instrumentation gives the publish call and the consume-and-react work their own spans, but nothing links them into one trace. Use this pattern whenever a trace needs to show the whole journey of one unit of work - “this order led to this stock update” - across an asynchronous boundary, not just each side in isolation.
flowchart LR
producer["Producer (Container)"]
broker[("Message broker / event stream (Container)")]
consumer["Consumer (Container)"]
tracing[("Trace storage (Container)")]
producer -->|"1: inject trace context into event payload"| broker
broker -->|"2: deliver event (payload includes trace context)"| consumer
consumer -->|"3: extract context, start child span"| consumer
producer -.->|exports spans| tracing
consumer -.->|exports spans, linked by extracted context| tracing
classDef container fill:#1168bd,stroke:#0b4884,color:#fff
class producer,broker,consumer,tracing container
Demonstrated concretely in this demo using OpenTelemetry’s Python SDK
(opentelemetry.propagate.inject()/extract()) over Redis pub/sub - the same inject/extract API
exists in every OpenTelemetry language SDK and is broker-agnostic; nothing about it depends on
Redis specifically. The anti-pattern worth naming directly: assuming auto-instrumentation alone
handles this. It doesn’t, for any broker without built-in trace-context propagation support -
and injecting the context but forgetting to extract it (or vice versa) produces no error
anywhere, just two disconnected traces that are easy to miss in code review.
Principle: Propagate trace context explicitly across asynchronous integrations
Statement. Any integration that crosses an asynchronous boundary - a message queue, a pub/sub topic, an event stream - must carry the producer’s trace context in the message itself, and the consumer must extract it and continue that trace, not start a new one. Auto-instrumentation of the publish/subscribe calls alone is not sufficient and must not be treated as though it were.
Rationale. Distributed tracing’s whole value is showing one unit of work’s full journey. Without explicit propagation, an asynchronous boundary silently breaks that journey into two unrelated traces that happen to share roughly the same timestamp - easy to miss in code review, and only obvious once someone is actually trying to debug a real incident by reading a trace that turns out to stop short of where the problem actually was.
Reference architecture: Saga / Compensating Transaction for a synchronous reservation
Added in Part 2 of this demo. When two services each own their own database, there’s no distributed transaction spanning both - a multi-step operation that touches both can succeed partway and leave them inconsistent. Use this pattern when a request needs to reserve a resource in one service, then run a check that can only happen after that reservation, and must undo the reservation cleanly if that check fails.
flowchart LR
caller["Caller (Container)"]
resourceOwner["Resource owner (Container)"]
store[("Reserved resource (Container)")]
caller -->|"1: reserve"| resourceOwner
resourceOwner -->|atomically decrements| store
caller -->|"2: downstream check"| caller
caller -->|"3a: check passed - proceed"| caller
caller -->|"3b: check failed - release"| resourceOwner
resourceOwner -->|undoes the reservation| store
classDef container fill:#1168bd,stroke:#0b4884,color:#fff
class caller,resourceOwner,store container
Demonstrated with a plain atomic SQL UPDATE ... WHERE stock >= ? (reserve) and its inverse
(release) over a synchronous HTTP call - no saga orchestration framework needed at this scale;
the caller itself is the orchestrator. The anti-pattern worth naming: treating release as a
no-op that can’t itself fail - if it fails, the reservation is left stuck with nothing giving it
back; a real implementation needs to handle that explicitly.
Reference architecture: Retry with Circuit Breaker for a synchronous dependency
Also added in Part 2. Retry alone means a persistent outage costs every caller the full retry price, on every request. Circuit Breaker alone means a single transient blip trips it unnecessarily. Composed together - circuit breaker wrapping the whole retry sequence as one logical attempt - each covers the other’s weak spot.
flowchart LR
caller["Caller (Container)"]
breaker{"Circuit breaker: closed / open / half-open"}
dependency["Dependency (Container)"]
caller -->|call| breaker
breaker -->|closed: attempt, up to 3x with backoff| dependency
breaker -->|open: fail immediately, no network call| caller
dependency -->|3 consecutive failed attempts| breaker
classDef container fill:#1168bd,stroke:#0b4884,color:#fff
class caller,dependency container
Hand-rolled in this demo rather than a library, deliberately, so the mechanism is visible to a reader rather than hidden behind a decorator - about 60 lines in total. Measured directly: three failed attempts each took roughly 8 seconds (a full retry sequence against a dead connection), then the circuit opened and a fourth request failed in under 100 milliseconds instead.
All four Part 2 concepts are themselves named entries in the glossary below - Saga, Compensating Transaction, Circuit Breaker, and Retry - demonstrated here working together, not replacing their canonical write-ups.
Reference architecture: CQRS: a read model built from an existing event
Added in Part 3 of this demo. Database-per-service means a query needing data from more than one service’s database can’t be answered with a single join. CQRS separates the model used to make changes from the model used to answer reads - the read side built in advance from events, shaped for the question it needs to answer, rather than computed live from the write side’s own tables.
flowchart LR
writer["Write side (Container)"]
writeDb[("Write model (Container)")]
event[("Domain event (Container)")]
projector["Read model projector (Container)"]
readDb[("Read model (Container)")]
reader["Query (Container)"]
writer -->|command| writeDb
writer -->|publishes| event
event -->|consumed by| projector
projector -->|builds/updates| readDb
reader -->|query| readDb
classDef container fill:#1168bd,stroke:#0b4884,color:#fff
class writer,writeDb,event,projector,readDb,reader container
Demonstrated here by reusing an event the write side already publishes for an unrelated reason (Part 2’s fulfilment log), rather than introducing a new event just for this - the simplest possible version of the pattern: one read model, one existing event source, same database as the write side’s own tables.
Reference architecture: Cache-Aside for a frequently-read, infrequently-changed value
Also added in Part 3. A value read far more often than it changes can be served from a cache most of the time, falling back to the real data store only on a miss.
flowchart LR
reader["Read (Container)"]
cache[("Cache (Container)")]
store[("Data store (Container)")]
writer["Write (Container)"]
reader -->|"1: check"| cache
cache -->|"2: miss - read, then populate"| store
writer -->|"on write: invalidate"| cache
classDef container fill:#1168bd,stroke:#0b4884,color:#fff
class reader,cache,store,writer container
Demonstrated here using Redis - already running in this architecture as the event broker, given a
second, unrelated job as a cache store. Measured directly: curl -i .../inventory twice in a row
shows MISS then HIT, and a write in between drops it back to MISS.
Both Part 3 concepts are themselves named entries in the glossary below - CQRS and Cache-Aside - demonstrated here at the smallest scale that still means something, not replacing their canonical write-ups.
Azure Cloud Design Patterns glossary
The full 44-pattern catalogue from Microsoft’s Azure Architecture Center - explicitly technology-agnostic despite “Azure” in the name, Microsoft’s own framing. Reproduced here in full since it’s already public reference material; Architecture Governance Platform links out to the same source rather than duplicating it.
| Pattern | Pillars | Summary |
|---|---|---|
| Ambassador | Reliability, Security | Create helper services that send network requests on behalf of a consumer service or application. |
| Anti-Corruption Layer | Operational Excellence | Implement a façade or adapter layer between a modern application and a legacy system. |
| Asynchronous Request-Reply | Performance Efficiency | Decouple back-end processing from a front-end host, for when back-end processing must be asynchronous but the front end requires a clear and timely response. |
| Backends for Frontends | Reliability, Security, Performance Efficiency | Create separate backend services for specific frontend applications or interfaces. |
| Bulkhead | Reliability, Security, Performance Efficiency | Isolate elements of an application into pools so that if one fails, the others continue to function. |
| Cache-Aside | Reliability, Performance Efficiency | Load data on demand into a cache from a data store. |
| Choreography | Operational Excellence, Performance Efficiency | Let individual services decide when and how a business operation is processed, instead of depending on a central orchestrator. |
| Circuit Breaker | Reliability, Performance Efficiency | Handle faults that might take a variable amount of time to fix when an application connects to a remote service or resource. |
| Claim Check | Reliability, Security, Cost Optimization, Performance Efficiency | Split a large message into a claim check and a payload to avoid overwhelming a message bus. |
| Compensating Transaction | Reliability | Undo the work performed by a sequence of steps that collectively form an eventually consistent operation. |
| Competing Consumers | Reliability, Cost Optimization, Performance Efficiency | Enable multiple concurrent consumers to process messages that they receive on the same messaging channel. |
| Compute Resource Consolidation | Cost Optimization, Operational Excellence, Performance Efficiency | Consolidate multiple tasks or operations into a single computational unit. |
| CQRS | Performance Efficiency | Separate operations that read data from those that update data by using distinct interfaces. |
| Deployment Stamps | Operational Excellence, Performance Efficiency | Deploy multiple independent copies of application components, including data stores. |
| Event Sourcing | Reliability, Performance Efficiency | Use an append-only store to record a full series of events that describe actions taken on data in a domain. |
| External Configuration Store | Operational Excellence | Move configuration information out of an application deployment package to a centralized location. |
| Federated Identity | Reliability, Security, Performance Efficiency | Delegate authentication to an external identity provider. |
| Gatekeeper | Security, Performance Efficiency | Protect applications and services by using a dedicated host instance to validate and sanitize requests before forwarding them to private back ends. |
| Gateway Aggregation | Reliability, Security, Operational Excellence, Performance Efficiency | Use a gateway to aggregate multiple individual requests into a single request. |
| Gateway Offloading | Reliability, Security, Cost Optimization, Operational Excellence, Performance Efficiency | Offload shared or specialized service functionality to a gateway proxy. |
| Gateway Routing | Reliability, Operational Excellence, Performance Efficiency | Route requests to multiple services by using a single endpoint. |
| Geode | Reliability, Performance Efficiency | Deploy back-end services across geographically distributed nodes. Each node can handle client requests from any region. |
| Health Endpoint Monitoring | Reliability, Operational Excellence, Performance Efficiency | Implement functional checks in an application that external tools can access through exposed endpoints at regular intervals. |
| Idempotent Consumer | Reliability | Handle duplicate message delivery so that processing a message multiple times has the same effect as processing it once. |
| Index Table | Reliability, Performance Efficiency | Create indexes over the fields in data stores that queries frequently reference. |
| Leader Election | Reliability | Coordinate actions in a distributed application by electing one instance as the leader, which manages a collection of collaborating task instances. |
| Materialized View | Performance Efficiency | Generate prepopulated views over the data in one or more data stores when the data is poorly formatted for required query operations. |
| Messaging Bridge | Cost Optimization, Operational Excellence | Build an intermediary to enable communication between messaging systems that are otherwise incompatible. |
| Pipes and Filters | Reliability | Break down a task that performs complex processing into a series of separate elements that can be reused. |
| Priority Queue | Reliability, Performance Efficiency | Prioritize requests sent to services so that requests with a higher priority are processed more quickly. |
| Publisher-Subscriber | Reliability, Security, Cost Optimization, Operational Excellence, Performance Efficiency | Enable an application to announce events to multiple consumers asynchronously, without coupling senders to receivers. |
| Quarantine | Security, Operational Excellence | Ensure that external assets meet a team-agreed quality level before the workload consumes them. |
| Queue-Based Load Leveling | Reliability, Cost Optimization, Performance Efficiency | Use a queue that creates a buffer between a task and a service to smooth intermittent heavy loads. |
| Rate Limiting | Reliability | Avoid or minimize throttling errors by controlling the consumption of resources. |
| Retry | Reliability | Enable applications to handle anticipated temporary failures by retrying failed operations. |
| Saga | Reliability | Manage data consistency across microservices in distributed transaction scenarios. |
| Scheduler Agent Supervisor | Reliability, Performance Efficiency | Coordinate a set of actions across distributed services and resources. |
| Sequential Convoy | Reliability | Process a set of related messages in a defined order without blocking other message groups. |
| Sharding | Reliability, Cost Optimization | Divide a data store into a set of horizontal partitions or shards. |
| Sidecar | Security, Operational Excellence | Deploy components into a separate process or container to provide isolation and encapsulation. |
| Static Content Hosting | Cost Optimization | Deploy static content to a cloud-based storage service for direct client delivery. |
| Strangler Fig | Reliability, Cost Optimization, Operational Excellence | Incrementally migrate a legacy system by gradually replacing pieces of functionality with new applications and services. |
| Throttling | Reliability, Security, Cost Optimization, Performance Efficiency | Control the consumption of resources from applications, tenants, or services. |
| Valet Key | Security, Cost Optimization, Performance Efficiency | Use a token or key to provide clients with restricted, direct access to a specific resource or service. |