AZ-305Chapter 40 of 103Objective 4.4

CQRS and Event Sourcing Patterns

This chapter covers the Command Query Responsibility Segregation (CQRS) and Event Sourcing patterns as they apply to designing scalable, auditable, and resilient solutions on Azure. These patterns are frequently tested in the AZ-305 exam under Objective 4.4: Design a data storage solution for transactional and analytical workloads. Approximately 5-10% of exam questions touch on CQRS and Event Sourcing concepts, often in the context of choosing between architectural patterns for high-volume systems or meeting compliance requirements for immutable audit logs.

25 min read
Intermediate
Updated May 31, 2026

The Bank Ledger and Separate Teller Slips

Imagine you walk into a bank to deposit $100. Instead of updating a single account balance, the bank does two things: First, a teller writes a deposit slip (the 'command') and hands it to a clerk who records the event 'Customer X deposited $100 on date Y' in a permanent, append-only ledger (event store). This ledger never erases or modifies entries—it only grows. Later, a separate system reads all events for Customer X from the ledger and computes the current balance as the sum of all deposits minus withdrawals (the 'projection'). If you ask the bank for your balance, you don't query the ledger directly; instead, you ask a 'read model' that was built from the ledger events. The key: the write operation (recording the event) is completely separate from the read operation (querying the balance). This separation allows the bank to have many different read models (e.g., daily balance, monthly statement, tax report) all built from the same immutable event stream. Critically, if the read model crashes, it can be rebuilt entirely from the ledger without any data loss. This is exactly how Command Query Responsibility Segregation (CQRS) and Event Sourcing work in software: commands produce events that are stored immutably, and separate read models are derived from those events.

How It Actually Works

What Are CQRS and Event Sourcing?

Command Query Responsibility Segregation (CQRS) is a pattern that separates read and write operations for a data store into different models. In traditional CRUD (Create, Read, Update, Delete) systems, the same entity model is used for both commands (writes) and queries (reads). CQRS breaks this symmetry: commands use a write-optimized model, while queries use a read-optimized model. This separation allows independent scaling, optimization, and security for each side.

Event Sourcing is a complementary pattern that stores all changes to an application's state as a sequence of immutable events. Instead of storing the current state of an entity (e.g., 'Customer balance: $500'), you store every event that led to that state (e.g., 'Account Opened', 'Deposit $100', 'Withdrawal $50'). The current state is derived by replaying all events for that entity. Event Sourcing naturally pairs with CQRS because the event store becomes the write model, and read models are built from the event stream.

Why These Patterns Exist

Traditional CRUD systems struggle under high contention, complex queries, and audit requirements. For example, in a banking system, if two users simultaneously update the same account balance, a lock is needed to avoid race conditions. With CQRS and Event Sourcing, writes are append-only and conflict-free (no locks), and reads are served from denormalized projections that can be queried without touching the write store. This architecture is ideal for:

High-volume transaction systems (e.g., payment processing)

Systems requiring a complete audit trail (e.g., financial compliance)

Systems with complex querying needs that would slow down writes

Systems where the read and write workloads have different performance and scaling requirements

How It Works Internally

Command Side (Write Model):

1.

A client sends a command (e.g., 'PlaceOrder'). The command is a request to perform an action, not a direct data modification.

2.

The command handler validates the command against business rules. If valid, it creates one or more events (e.g., 'OrderPlaced', 'InventoryReserved').

3.

Each event is appended to an event store. The event store is an append-only log, typically implemented using a database like Azure Cosmos DB, Azure SQL Database, or a specialized event store like EventStoreDB.

4.

The event is published to an event bus (e.g., Azure Event Grid, Azure Service Bus) for consumption by read-side projections and other services.

Read Side (Read Model):

1.

A projection (or subscriber) listens to events from the event bus.

2.

The projection updates a read-optimized data store (e.g., Azure Cosmos DB, Azure Cache for Redis, Azure SQL Database with indexed views). This store is shaped for query efficiency—often denormalized, with pre-computed aggregates.

3.

Clients query the read model directly. They never query the event store for current state.

Key Components and Defaults:

Event Store: Append-only log. In Azure, Cosmos DB is commonly used because it offers change feed (which can act as an event bus) and supports large volumes. Default consistency: Session consistency for writes; eventual consistency for reads is acceptable because the read model is updated asynchronously.

Event Bus: Azure Event Grid (for event routing) or Azure Service Bus (for reliable messaging with at-least-once delivery). Default TTL for Service Bus messages: 14 days max.

Projections: Stateless services that read events and update read models. They can be implemented as Azure Functions, Azure Container Instances, or AKS pods.

Read Model Store: Can be Cosmos DB (with low latency reads), Azure Cache for Redis (for ultra-fast reads), or Azure SQL Database (for complex queries).

Configuration Example (Azure Cosmos DB as Event Store):

To use Cosmos DB as an event store, create a container with a partition key on the aggregate ID (e.g., /orderId). Enable the change feed to publish events. The change feed can be consumed by an Azure Function that updates the read model.

{
  "id": "order-container",
  "partitionKey": {
    "paths": ["/orderId"],
    "kind": "Hash"
  },
  "defaultTtl": -1,
  "changeFeedPolicy": {
    "enabled": true
  }
}

Verification Commands (Azure CLI):

Check if change feed is enabled:

az cosmosdb sql container show --resource-group myRG --account-name myAccount --database-name myDB --name myContainer --query "resource.changeFeedPolicy"

List events from change feed (using Azure Functions SDK):

[FunctionName("OrderProjection")]
public static async Task Run([CosmosDBTrigger(
    databaseName: "myDB",
    collectionName: "myContainer",
    ConnectionStringSetting = "CosmosDBConnection",
    LeaseCollectionName = "leases",
    CreateLeaseCollectionIfNotExists = true)]IReadOnlyList<Document> documents, ILogger log)
{
    // Process each document as an event
}

How It Interacts with Related Technologies

Azure Functions: Often used to implement command handlers and projections. Functions can be triggered by Event Grid, Service Bus, or Cosmos DB change feed.

Azure Event Grid: Acts as the event bus for publishing events to multiple subscribers. Supports at-least-once delivery.

Azure Service Bus: Used when reliable, ordered delivery is needed. Topics and subscriptions allow fan-out.

Azure Cosmos DB: Can serve as both event store (with change feed) and read model store (with low-latency point reads).

Azure Cache for Redis: Used for read models that need sub-millisecond latency, e.g., a leaderboard.

Azure SQL Database: Suitable for read models requiring complex JOINs or reporting.

Performance and Scaling Considerations

Write Scaling: The event store is append-only, so writes are fast and can be scaled horizontally by partitioning on aggregate ID. Cosmos DB can handle millions of writes per second with proper partition design.

Read Scaling: Read models can be scaled independently, often using multiple replicas or caching layers. Since reads don't touch the event store, they don't compete with writes.

Eventual Consistency: Because projections update asynchronously, the read model may lag behind the write model. For most systems, this is acceptable. The exam tests that you understand this trade-off.

Snapshotting: To avoid replaying all events from the beginning, snapshots of aggregate state can be taken periodically. A snapshot is a saved state at a specific event version. When replaying, start from the latest snapshot and replay only subsequent events. Default snapshot frequency: every 100 events (configurable).

Common Pitfalls

Over-engineering: CQRS and Event Sourcing add complexity. They are not suitable for simple CRUD applications. The exam expects you to know when to use them (high contention, audit requirements, complex queries) and when not to (simple, low-volume systems).

Ignoring Eventual Consistency: Business processes must tolerate stale reads. For example, a user may not immediately see their own update. The exam tests this with scenarios like 'After placing an order, the user checks order status and sees no change for a few seconds.'

Event Schema Evolution: Events are immutable, so changing an event schema requires versioning (e.g., OrderPlacedV2). The exam may ask about handling schema changes.

Step-by-Step: Implementing CQRS with Event Sourcing on Azure

1.

Define Commands and Events – Identify the actions (commands) and state changes (events) in your domain. For example, 'AddItemToCart' command produces 'ItemAddedToCart' event.

2.

Create the Event Store – Use Azure Cosmos DB with a container per aggregate type. Set partition key to aggregate ID. Enable change feed.

3.

Implement Command Handlers – Write Azure Functions that receive commands (e.g., via HTTP trigger), validate, and append events to the event store.

4.

Implement Projections – Write Azure Functions triggered by Cosmos DB change feed. Each projection updates a read model.

5.

Create Read Models – Store denormalized data in Cosmos DB, Redis, or SQL Database. Design for query patterns.

6.

Set Up Event Bus (Optional) – If multiple projections need to consume events, use Event Grid or Service Bus to fan out.

7.

Handle Failures – Implement idempotency in projections (events may be delivered more than once). Use poison message handling for malformed events.

8.

Test for Consistency – Verify that read models eventually converge. Monitor lag using application insights.

Exam Relevance

On the AZ-305 exam, CQRS and Event Sourcing appear in the context of designing data storage solutions (Objective 4.4). You may be asked to recommend a pattern for a scenario with high write throughput, audit requirements, or complex queries. You must know:

The difference between CQRS and Event Sourcing (CQRS separates read/write models; Event Sourcing stores events as the source of truth).

When to use each (CQRS alone for simple read/write separation; Event Sourcing when audit trail is needed).

Azure services that support these patterns (Cosmos DB, Event Grid, Service Bus, Functions).

Trade-offs: eventual consistency, complexity, storage costs (event stores grow over time).

Common wrong answers include suggesting CQRS for low-volume systems (over-engineering) or using Event Sourcing without CQRS (possible but less common). The exam expects you to recognize that Event Sourcing almost always pairs with CQRS.

Walk-Through

1

Define Commands and Events

Start by identifying all user actions that change state (commands) and the resulting state changes (events). For example, in an e-commerce system, a 'SubmitOrder' command produces an 'OrderSubmitted' event. Each event captures what happened (e.g., OrderId, CustomerId, Items, Timestamp). Events are past-tense verbs and immutable. This step is crucial because events define the contract for the entire system. A common mistake is to design events that mirror database tables rather than business occurrences. Events should be granular: 'ItemAddedToCart' not 'CartUpdated'. In the exam, scenarios often describe business processes; you must extract the correct events.

2

Create the Event Store

Provision an Azure Cosmos DB account and create a database with a container per aggregate type. For example, an 'Orders' container with partition key '/orderId'. Enable the change feed to capture events as they are written. Set default TTL to -1 (no expiration) because events are permanent. Use Session consistency for writes to ensure causal ordering within a session. The event store must support high write throughput; Cosmos DB can scale to millions of writes per second. In the exam, you may be asked why Cosmos DB is chosen over SQL Database for event sourcing: answer is its change feed and horizontal scalability.

3

Implement Command Handlers

Command handlers are functions that receive commands (e.g., via HTTP POST), validate them against business rules, and append events to the event store. They are typically implemented as Azure Functions with an HTTP trigger. The handler loads the current state by replaying events for the aggregate (or using a snapshot), applies business logic, and then appends new events. If validation fails, the command is rejected and no event is stored. This ensures that only valid state transitions are recorded. In the exam, remember that commands are imperative (e.g., 'PlaceOrder') and can fail; events are declarative and always succeed once stored.

4

Implement Projections

Projections are subscribers to the event stream that update read models. They can be Azure Functions triggered by Cosmos DB change feed or by Event Grid/Service Bus messages. A projection reads an event, transforms it, and updates a read-optimized store. For example, an 'OrderSummaryProjection' listens to 'OrderSubmitted' events and increments a daily order count in a Redis cache. Projections must be idempotent: if the same event is delivered twice, the read model should not be corrupted. This is achieved by using upsert operations or tracking processed event IDs. The exam tests understanding that projections are eventually consistent and can lag behind writes.

5

Create Read Models

Read models are denormalized data structures optimized for query patterns. They are stored in a separate database from the event store. For example, a 'CustomerOrders' read model might contain a list of recent orders with status, total, and timestamp—all pre-joined. Use Azure Cosmos DB for flexible schema and low latency, or Azure Cache for Redis for ultra-fast reads. Read models can be rebuilt from scratch by replaying all events from the event store. This is a key benefit: if a read model becomes corrupted, it can be regenerated without data loss. In the exam, you may be asked to choose a read model store based on query requirements: Cosmos DB for point reads, SQL for complex queries, Redis for high-speed caching.

What This Looks Like on the Job

Enterprise Scenarios

Scenario 1: Financial Trading Platform

A large bank processes millions of trades per day. Each trade must be recorded immutably for regulatory compliance (e.g., MiFID II). The bank uses Event Sourcing with Azure Cosmos DB as the event store. Each trade event (e.g., 'TradeExecuted', 'SettlementCompleted') is appended to a container partitioned by trade ID. The write throughput is massive—up to 100,000 events per second. Cosmos DB handles this with multiple partitions and automatic scaling. The read side uses Azure Cache for Redis to serve real-time portfolio balances (read models) with sub-millisecond latency. Projections are implemented as Azure Functions triggered by Cosmos DB change feed. A common misconfiguration is setting the Cosmos DB container's default TTL to a finite value, causing events to be deleted prematurely. In production, TTL is set to -1 for the event store, and a separate archival process moves old events to Azure Blob Storage for long-term retention.

Scenario 2: E-Commerce Order Management

An online retailer uses CQRS (without full Event Sourcing) to handle high-volume order placement and inventory queries. The write side uses Azure SQL Database with optimized indexes for fast inserts. The read side uses Azure Cosmos DB with denormalized order summaries for fast customer queries. This separation allows the inventory query (read) to scale independently from order placement (write). During Black Friday, the read side is scaled out to 10 replicas, while the write side remains on a single high-performance SQL database. The challenge is eventual consistency: after an order is placed, the customer may not see it in their order history for a few seconds. The business accepts this because the trade-off is higher throughput. In the exam, this scenario tests whether you recognize that CQRS alone (without Event Sourcing) is sufficient when an audit trail is not required.

Scenario 3: Healthcare Patient Records

A hospital system needs a complete audit trail of all changes to patient records for HIPAA compliance. They implement Event Sourcing with Azure Service Bus as the event bus. Each event (e.g., 'DiagnosisAdded', 'MedicationPrescribed') is published to a topic. Multiple subscriptions update different read models: one for the clinical dashboard (Cosmos DB), one for billing (SQL Database), and one for research analytics (Azure Data Lake). The event store is Azure Cosmos DB with change feed. A critical design consideration is event schema versioning: when a new field is added to an event, a new event type (e.g., 'DiagnosisAddedV2') is created, and projections handle both versions. Misconfiguration often occurs when developers forget to handle old event versions, causing projections to fail. The exam tests understanding of schema evolution and the need for versioning.

How AZ-305 Actually Tests This

AZ-305 Exam Focus on CQRS and Event Sourcing

Objective Code: 4.4 – Design a data storage solution for transactional and analytical workloads. Sub-objectives include recommending a data storage solution for transactional workloads and for analytical workloads. CQRS and Event Sourcing are explicitly listed as patterns to consider.

Common Wrong Answers:

1.

'Use CQRS for all systems because it improves performance.' – Wrong. CQRS adds complexity. It is only beneficial when read and write workloads have different scaling or optimization needs. The exam expects you to identify scenarios where CQRS is overkill (e.g., a simple blog with low traffic).

2.

'Event Sourcing requires a relational database for the event store.' – Wrong. Event stores are append-only logs; relational databases work but are not ideal. Cosmos DB is preferred for its change feed and scalability. The exam tests Azure-specific services.

3.

'In CQRS, the read model always has the latest data.' – Wrong. Read models are eventually consistent. The exam loves to test this: a question may describe a user not seeing their own update and ask why. The correct answer is eventual consistency, not a bug.

4.

'CQRS and Event Sourcing are the same pattern.' – Wrong. CQRS separates read and write models; Event Sourcing stores events as the source of truth. They are often used together but are distinct. The exam may ask which pattern provides an audit trail (Event Sourcing).

Specific Numbers and Terms:

Eventual consistency: The default consistency model for read models. No specific timeout; lag depends on system load.

Snapshot frequency: Typically every 100 events (configurable). Not directly tested but useful for understanding performance.

Azure services: Cosmos DB (event store with change feed), Event Grid (event bus), Service Bus (reliable messaging), Functions (handlers/projections).

Partition key: For event store, use aggregate ID (e.g., orderId) to ensure all events for an entity are in the same partition.

Edge Cases and Exceptions:

Idempotency: Events may be delivered more than once. Projections must handle duplicates (e.g., using upsert or tracking processed event IDs).

Schema evolution: When an event schema changes, create a new version (e.g., 'OrderPlacedV2'). Old projections must still handle old versions.

Eventual consistency vs. strong consistency: If the business requires immediate consistency (e.g., banking balance after transfer), CQRS may not be appropriate. The exam tests this trade-off.

How to Eliminate Wrong Answers:

If the scenario mentions 'audit trail' or 'immutable log', Event Sourcing is required.

If the scenario mentions 'different read and write workloads', CQRS is indicated.

If the scenario is simple and low-volume, neither pattern is needed.

Always check if the question asks for a pattern to 'improve query performance' vs. 'ensure data integrity'. CQRS helps queries; Event Sourcing helps integrity.

Key Takeaways

CQRS separates read and write models; Event Sourcing stores state changes as an immutable event log.

Event Sourcing provides a built-in audit trail because all changes are recorded as events.

In CQRS, read models are eventually consistent with the write model; there is no synchronous update.

Azure Cosmos DB with change feed is the primary Azure service for implementing event sourcing.

Azure Functions are commonly used for command handlers and projections in CQRS/Event Sourcing architectures.

CQRS and Event Sourcing are not suitable for simple CRUD applications; they add unnecessary complexity.

Snapshotting (e.g., every 100 events) improves performance by avoiding full event replay.

Event schema must be versioned to handle changes over time.

Easy to Mix Up

These come up on the exam all the time. Here's how to tell them apart.

CQRS Only

Separates read and write models but uses a single source of truth (e.g., SQL database) for both.

Commands update the write model directly; reads query the read model (e.g., a denormalized view).

No immutable event log; audit trail is not naturally provided.

Simpler to implement; less storage overhead.

Suitable when audit trail is not required and eventual consistency is acceptable.

CQRS + Event Sourcing

Uses an event store as the source of truth; read models are derived from events.

Commands produce events stored in an append-only log; projections update read models.

Provides a complete audit trail via the event stream.

More complex; requires event schema management and snapshotting.

Suitable for systems requiring auditability, high write throughput, or complex event-driven workflows.

Watch Out for These

Mistake

CQRS and Event Sourcing must always be used together.

Correct

They are independent patterns. CQRS can be implemented without Event Sourcing (e.g., using separate databases for read and write). Event Sourcing is often paired with CQRS but can be used alone. The exam tests that you can recommend one without the other.

Mistake

In CQRS, the read model is updated synchronously with the write model.

Correct

Read models are updated asynchronously via event subscriptions. This introduces eventual consistency. The read model may lag behind the write model by milliseconds to seconds depending on system load.

Mistake

Event Sourcing stores the current state of an entity.

Correct

Event Sourcing stores a sequence of events that represent state changes. The current state is derived by replaying all events. The event store never stores the current state directly.

Mistake

CQRS is a good fit for all high-traffic systems.

Correct

CQRS adds complexity (two models, eventual consistency, more infrastructure). It is only beneficial when read and write workloads have significantly different patterns. For many high-traffic systems, a well-designed single database with caching suffices.

Mistake

The event store in Event Sourcing can be a traditional SQL database with UPDATE operations.

Correct

The event store must be append-only with no updates or deletes. Traditional SQL databases allow UPDATE, which violates immutability. Azure Cosmos DB or a dedicated event store is preferred because they enforce append-only semantics.

Do You Actually Know This?

Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.

Frequently Asked Questions

What is the difference between CQRS and Event Sourcing?

CQRS (Command Query Responsibility Segregation) is a pattern that separates read and write operations into different models, allowing independent scaling and optimization. Event Sourcing is a pattern that stores all state changes as a sequence of immutable events, providing an audit trail and enabling state reconstruction. They are often used together but are independent: you can have CQRS without Event Sourcing (e.g., separate databases) and Event Sourcing without CQRS (though less common).

When should I use CQRS on Azure?

Use CQRS when your application has significantly different read and write workloads. For example, if writes are frequent and queries are complex, or if you need to scale reads independently. Avoid CQRS for simple CRUD applications where a single database with caching suffices. The AZ-305 exam expects you to identify scenarios like high-volume transaction processing or systems with complex reporting needs.

Which Azure services support Event Sourcing?

Azure Cosmos DB is the primary service for Event Sourcing due to its change feed (which acts as an event bus) and horizontal scalability. Azure Event Grid and Azure Service Bus can be used as event buses to distribute events to projections. Azure Functions are commonly used to implement command handlers and projections.

How do I handle eventual consistency in CQRS?

Design your application to tolerate stale data. For example, after a user submits an order, show a confirmation message and poll the read model for updates. Use techniques like optimistic UI updates (showing the expected state immediately) and background refresh. In the exam, recognize that eventual consistency is a trade-off for improved performance and scalability.

Can I use Event Sourcing without CQRS?

Yes, but it is less common. You could store events and query them directly, but that would be inefficient for complex queries. Typically, Event Sourcing is paired with CQRS to build separate read models from the event stream. The exam may present a scenario where Event Sourcing is used for audit purposes, and CQRS is not explicitly needed if queries are simple.

What is a snapshot in Event Sourcing?

A snapshot is a saved state of an aggregate at a specific event version. Instead of replaying all events from the beginning to get the current state, you load the latest snapshot and replay only events after that snapshot. This improves performance. A common snapshot frequency is every 100 events, but it is configurable based on performance requirements.

How do I handle event schema changes?

Events are immutable, so you cannot modify existing events. Instead, create a new version of the event type (e.g., 'OrderPlacedV2') with the new fields. Projections must handle both old and new versions. Use a version field in the event schema to differentiate. The exam may test your understanding of schema evolution.

Terms Worth Knowing

Ready to put this to the test?

You've just covered CQRS and Event Sourcing Patterns — now see how well it sticks with free AZ-305 practice questions. Full explanations included, no account needed.

Done with this chapter?