DVA-C02Chapter 93 of 101Objective 1.6

Event Sourcing and CQRS Patterns on AWS

This chapter covers Event Sourcing and CQRS (Command Query Responsibility Segregation) patterns on AWS, two advanced architectural patterns that are increasingly relevant for the DVA-C02 exam. While not a dominant topic, they appear in scenario-based questions about designing scalable, auditable systems with high write throughput and complex read requirements. Understanding these patterns helps you choose the right AWS services for event-driven architectures, data consistency trade-offs, and building decoupled systems. Expect 1-3 exam questions that test your ability to identify when to use event sourcing or CQRS, and which AWS services (DynamoDB, Kinesis, Lambda, SQS) implement them.

25 min read
Intermediate
Updated May 31, 2026

The Bank Ledger vs. Balance Snapshot

Imagine a bank where every transaction is recorded in an immutable ledger: deposits, withdrawals, transfers—each entry appended in order with a timestamp and a unique ID. The bank never updates a customer's balance directly; instead, the current balance is always computed by replaying all transactions for that account from the beginning. This is event sourcing. Now, suppose the bank also maintains a separate, read-optimized view for daily statements that aggregates transactions by category and date—this view is updated asynchronously from the event stream, not by querying the ledger directly. That's CQRS. The ledger is the write model (commands), and the statement is the read model (queries). If a teller needs to see the current balance quickly, they don't replay the entire ledger—they use a precomputed snapshot. But if there's a dispute, they go back to the immutable ledger for full auditability. On AWS, you might use Amazon DynamoDB as the event store (append-only), AWS Lambda to process events and update read models in Amazon ElastiCache or Aurora, and Amazon Kinesis or DynamoDB Streams to capture events reliably. Just like the bank, you never delete or modify past events—only append new ones. This pattern ensures full history, strong consistency for writes, and eventual consistency for reads optimized for specific query patterns.

How It Actually Works

What is Event Sourcing?

Event sourcing is a pattern where you store the state of an application as a sequence of immutable events, rather than storing the current state directly. Each event represents a state change (e.g., 'OrderPlaced', 'PaymentReceived', 'ItemShipped'). To get the current state, you replay all events from the beginning (or from a snapshot). The event store is the single source of truth.

Why Event Sourcing?

Audit trail: Every change is recorded immutably. You can reconstruct any past state.

Temporal queries: You can ask 'what was the state at time T?' by replaying events up to that point.

Event-driven integration: Other services can subscribe to the event stream (e.g., via DynamoDB Streams or Kinesis).

Conflict resolution: In distributed systems, events can be merged without locking (e.g., using last-writer-wins or CRDTs).

How Event Sourcing Works on AWS

Amazon DynamoDB is a popular choice for an event store. The table has a partition key (e.g., aggregate ID) and a sort key (e.g., event sequence number or timestamp). Each item is an event with attributes like event type, data, and metadata. The table is append-only—you never update or delete events. To get the current state, you query all events for a given aggregate ID, sort by sequence number, and apply them in order.

Example event schema in DynamoDB:

{
  "aggregateId": "order-123",
  "eventId": "event-456",
  "eventType": "OrderPlaced",
  "data": { "customerId": "cust-789", "total": 100.00 },
  "timestamp": 1700000000
}

To optimize reads, you can take periodic snapshots of the aggregate state and store them in a separate table or in the same table with a different sort key prefix (e.g., 'SNAPSHOT#'). When replaying, you start from the latest snapshot and only replay events after it.

DynamoDB Streams and Event Sourcing

DynamoDB Streams captures a time-ordered sequence of item-level changes in a DynamoDB table. When you use DynamoDB as an event store, you can enable streams to push events to AWS Lambda, Kinesis, or other consumers. This enables event-driven processing and integration with other services.

Important: DynamoDB Streams records changes in near-real-time (typically within seconds). The stream retains data for 24 hours. Each shard processes events in order, but across shards order is not guaranteed.

What is CQRS?

CQRS stands for Command Query Responsibility Segregation. It separates the operations that modify data (commands) from those that read data (queries). In a traditional CRUD system, the same model handles both reads and writes. In CQRS, you have separate models, often backed by separate data stores.

Why CQRS?

Different performance requirements: Writes may need strong consistency; reads may tolerate eventual consistency.

Different schemas: Write model is normalized for integrity; read model is denormalized for fast queries.

Security: Commands can enforce business rules; queries can have restricted views.

Scalability: Write side can be scaled independently from read side.

CQRS on AWS

A common implementation uses: - Write store: DynamoDB table with events (event sourcing) or a relational database. - Read store: Amazon ElastiCache, DynamoDB Global Tables, or Amazon Aurora Read Replicas. - Event bus: Amazon EventBridge or SNS/SQS to propagate events from write side to read side.

Example: An e-commerce system writes orders to a DynamoDB table (event store). A Lambda function triggered by DynamoDB Streams updates a read-optimized table in DynamoDB that stores order summaries by customer, or updates an ElastiCache cluster for low-latency reads.

Combining Event Sourcing and CQRS

Event sourcing naturally pairs with CQRS. The event store is the write model (commands append events). The read model is built by subscribing to the event stream and projecting the events into a denormalized view. This is called event projection.

Projection example: A Lambda function listens to DynamoDB Streams, reads 'OrderPlaced' and 'ItemShipped' events, and updates a DynamoDB table that tracks order status by customer ID. The read table might have attributes like customerId, orderId, status, lastUpdated.

Key Terms and Values

Event: Immutable fact about something that happened. Has a type, timestamp, and data.

Event store: Database that stores events, append-only. DynamoDB (with streams) is common.

Snapshot: A saved state of an aggregate at a point in time, used to speed up replays.

Projection: A function that takes events and updates a read model.

Command: An instruction to perform an action. Validated and then converted to an event.

Query: A request for data. Does not change state.

DynamoDB as Event Store: Best Practices

Use partition key as aggregate ID (e.g., customerId).

Use sort key as a sequence number or timestamp. If using timestamp, ensure uniqueness (add UUID if needed).

Enable DynamoDB Streams with 'New image' to capture the event as it is written.

Set TTL on events if you need to purge old data (but this breaks temporal queries for past states).

Use conditional writes to enforce idempotency (e.g., check if event ID already exists).

Amazon Kinesis for Event Streaming

Kinesis Data Streams is another option for event sourcing. Events are published to a shard, and consumers read them in order. Kinesis retains records for up to 365 days (default 24 hours). You can use Kinesis Data Analytics for real-time processing or Kinesis Data Firehose to land events in S3 for archival.

Kinesis vs. DynamoDB Streams: - DynamoDB Streams is tightly coupled to DynamoDB tables, simpler for single-table event sourcing. - Kinesis is more flexible for high-throughput, multi-producer, multi-consumer scenarios. - Kinesis has higher per-shard throughput (1 MB/s write, 2 MB/s read).

Consistency Models

Event sourcing typically uses eventual consistency for read models. The write side is strongly consistent if you use DynamoDB with strongly consistent reads (but this adds cost). CQRS often embraces eventual consistency: after a command is processed, the read model is updated asynchronously. The exam tests your understanding of when eventual consistency is acceptable.

Idempotency

Commands should be idempotent to handle retries. Use a unique request ID and store it alongside the event. On duplicate command, return the existing event without creating a new one.

Snapshots

To avoid replaying all events from the beginning, take periodic snapshots. In DynamoDB, you can store a snapshot item with sort key 'SNAPSHOT#<sequence>'. When loading an aggregate, first load the latest snapshot, then replay events after that snapshot's sequence number.

Integration with AWS Services

Amazon EventBridge: Acts as an event bus. Commands can produce events that are routed to targets (e.g., Lambda, SQS).

AWS Lambda: Common for processing events and updating read models.

Amazon SQS: Decouples command handling from event processing. Use FIFO queues for ordering.

Amazon S3: Store large event payloads or archives.

Amazon ElastiCache: In-memory read model for low-latency queries.

Example Architecture

1.

Client sends a command (e.g., 'PlaceOrder') to an API Gateway endpoint.

2.

API Gateway invokes a Lambda function that validates the command and writes an 'OrderPlaced' event to a DynamoDB table (event store).

3.

DynamoDB Streams captures the new event and triggers a second Lambda function.

4.

This Lambda function updates a read-optimized DynamoDB table (e.g., 'OrderSummaryByCustomer').

5.

Client queries the read table via another API endpoint to get order status.

Exam Relevance

For DVA-C02, you need to:

Recognize scenarios where event sourcing is appropriate (audit trail, temporal queries, complex state reconstruction).

Recognize scenarios for CQRS (different read/write workloads, need for denormalized reads).

Identify that DynamoDB Streams enables event-driven projections.

Understand that event sourcing and CQRS often lead to eventual consistency.

Know that you can use DynamoDB as an event store with append-only writes.

Be aware that Kinesis can also serve as an event stream.

Common Pitfalls

Using a relational database with UPDATE statements: That's not event sourcing; you lose history.

Not handling event ordering: In distributed systems, events may arrive out of order. Use sequence numbers or timestamps with logic to reject old events.

Overcomplicating: Not every system needs event sourcing. For simple CRUD, it adds complexity.

Mixing read and write models: In CQRS, never query the event store for read models; use projections.

Code Example: Writing an Event to DynamoDB

import boto3
import uuid
import time

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('EventStore')

def append_event(aggregate_id, event_type, data):
    event_id = str(uuid.uuid4())
    timestamp = int(time.time())
    table.put_item(
        Item={
            'aggregateId': aggregate_id,
            'eventId': f'{timestamp}#{event_id}',
            'eventType': event_type,
            'data': data,
            'timestamp': timestamp
        },
        ConditionExpression='attribute_not_exists(eventId)'
    )
    return event_id

Code Example: Replaying Events to Get Current State

def get_aggregate_state(aggregate_id):
    response = table.query(
        KeyConditionExpression=Key('aggregateId').eq(aggregate_id),
        ScanIndexForward=True  # ascending order by sort key
    )
    state = {}
    for event in response['Items']:
        # apply event to state (domain logic)
        if event['eventType'] == 'OrderPlaced':
            state['status'] = 'placed'
            state['total'] = event['data']['total']
        elif event['eventType'] == 'PaymentReceived':
            state['status'] = 'paid'
    return state

Walk-Through

1

Client Sends Command

A client (e.g., a web application) sends a command to the system, such as 'PlaceOrder'. The command is an instruction to perform an action, not a fact. It is typically sent via an API (API Gateway) to a command handler (Lambda). The handler validates the command against business rules. If valid, it proceeds to create an event.

2

Command Handler Writes Event

The command handler creates an immutable event object (e.g., 'OrderPlaced') and appends it to the event store (DynamoDB table). The event includes a unique ID, aggregate ID, event type, data payload, and timestamp. The write is conditional to ensure idempotency. The event store is append-only; no update or delete occurs.

3

Event Store Captures via Stream

DynamoDB Streams captures the new item change. The stream record contains the new image (the event). Streams are enabled per table and can be configured to capture only new images. The stream shards process events in near-real-time. A Lambda function is triggered with a batch of stream records.

4

Lambda Processes Event (Projection)

The Lambda function receives the stream record, extracts the event, and updates the read model (e.g., an OrderSummary table in DynamoDB or a cache in ElastiCache). This is called an event projection. The read model is denormalized for fast queries. The function may also publish the event to EventBridge for further processing.

5

Client Queries Read Model

The client sends a query (e.g., 'GetOrderStatus') to a separate read endpoint. The read handler retrieves data from the read model (e.g., DynamoDB table or ElastiCache). The read model is eventually consistent with the event store. The client gets a fast response without touching the event store.

What This Looks Like on the Job

Enterprise Scenario 1: Financial Ledger System

A fintech company needs an immutable audit trail for all transactions. They use event sourcing with DynamoDB as the event store. Each transaction (deposit, withdrawal, transfer) is appended as an event. The read side uses CQRS: a DynamoDB table stores current balances per account, updated asynchronously via Lambda and DynamoDB Streams. This allows real-time balance checks while maintaining full history. At scale (millions of transactions per day), they use DynamoDB auto-scaling and DAX for read caching. A common misconfiguration is not using conditional writes, leading to duplicate events. They also snapshot aggregates daily to avoid replaying millions of events on every load.

Enterprise Scenario 2: E-Commerce Order Management

An e-commerce platform uses CQRS to separate order placement (write) from order tracking (read). The write side uses DynamoDB with event sourcing. The read side uses an ElastiCache Redis cluster to provide low-latency order status for customers. When an order is placed, a Lambda function writes the event to DynamoDB and also updates Redis. Another Lambda processes the stream to update a reporting database in Aurora. The system handles 10,000 orders per minute. A pitfall is not handling eventual consistency: customers occasionally see stale statuses. They mitigate by adding optimistic locking and a 'lastUpdated' timestamp for conflict resolution.

Enterprise Scenario 3: IoT Device State Tracking

An IoT platform tracks device states (on/off, temperature) using event sourcing. Each device publishes events to Kinesis Data Streams. A Lambda function stores the events in DynamoDB (event store) and updates a read-optimized table with the latest state per device. The read table is queried by a dashboard. They use Kinesis because of high throughput (100,000 events/second). A misconfiguration is forgetting to set the retention period on Kinesis, causing data loss after 24 hours. They set retention to 7 days for replay capability.

How DVA-C02 Actually Tests This

What DVA-C02 Tests

The exam focuses on identifying appropriate use cases for event sourcing and CQRS, and selecting the correct AWS services to implement them. Key objective codes: Domain 1 (Development) Objective 1.6 – Implement event-driven architectures. You may also see it in Domain 4 (Monitoring and Troubleshooting) for event replay scenarios.

Common Wrong Answers

1.

'Use a relational database with UPDATE statements' – Candidates choose this because they are familiar with CRUD. But event sourcing requires append-only, immutable storage. The correct answer is DynamoDB with conditional writes.

2.

'Use SQS for the event store' – SQS is a queue, not a durable event store. Events are consumed and deleted. The exam expects a persistent store like DynamoDB or Kinesis.

3.

'CQRS means you must have separate databases' – While common, CQRS can be implemented with separate tables in the same database. The exam may test that the read and write models are separate, not necessarily the physical storage.

4.

'Event sourcing guarantees strong consistency' – Event sourcing itself does not guarantee strong consistency for reads; it's often used with eventual consistency. The exam may ask about trade-offs.

Specific Numbers and Terms

DynamoDB Streams retention: 24 hours.

Kinesis Data Streams retention: default 24 hours, max 365 days.

Kinesis per-shard throughput: 1 MB/s write, 2 MB/s read.

DynamoDB strongly consistent reads: eventually consistent reads are cheaper and default.

Lambda trigger batch size for DynamoDB Streams: up to 10,000 records; max payload 6 MB.

Edge Cases

Out-of-order events: When using Kinesis, events within a shard are ordered. Across shards, order is not guaranteed. The exam may test that you need a single shard or a sequence number to maintain global order.

Idempotency: Commands must be idempotent. Use a unique request ID and store it in the event to detect duplicates.

Snapshot frequency: Too frequent snapshots increase write costs; too infrequent increase replay time. There is no default; you must tune based on aggregate size.

How to Eliminate Wrong Answers

If the question mentions 'audit trail', 'history', or 'temporal query', event sourcing is likely correct.

If the question separates read and write workloads with different performance needs, CQRS is likely.

If the answer suggests using a single database for both reads and writes with the same schema, it's probably not CQRS.

If the answer uses SQS for long-term storage, eliminate it—SQS is transient.

Key Takeaways

Event sourcing stores changes as an immutable sequence of events; the current state is derived by replaying events.

CQRS separates commands (writes) from queries (reads), often with different models and data stores.

On AWS, DynamoDB is a common event store; enable DynamoDB Streams to trigger Lambda for projections.

Kinesis Data Streams can serve as an event stream with up to 365-day retention and higher throughput than DynamoDB Streams.

Commands must be idempotent; use a unique request ID stored in the event to handle retries.

Snapshots reduce replay time; store them in the same DynamoDB table with a different sort key prefix.

Event sourcing and CQRS often lead to eventual consistency for read models; the exam tests this trade-off.

DynamoDB Streams retains records for 24 hours; Kinesis default is 24 hours, max 365 days.

The exam expects you to identify scenarios requiring audit trails or temporal queries as candidates for event sourcing.

CQRS is appropriate when read and write workloads have different performance, scalability, or security requirements.

Easy to Mix Up

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

Event Sourcing

Stores state as a sequence of immutable events.

Full audit trail – every change is recorded.

Can reconstruct any past state by replaying events.

Works well with event-driven architectures.

Complexity: requires event replay and snapshot management.

Traditional CRUD

Stores current state directly, overwriting previous values.

No built-in audit trail – history is lost on update.

Cannot easily answer temporal queries without additional logging.

Simpler to implement for basic applications.

Better for simple CRUD with no need for history.

Watch Out for These

Mistake

Event sourcing requires a separate event store like Kafka or Kinesis.

Correct

You can use DynamoDB as an event store with append-only writes and streams. Kinesis is also an option, but DynamoDB is simpler for many use cases and is a common exam answer.

Mistake

CQRS means you must have two separate databases (e.g., one for writes, one for reads).

Correct

CQRS separates the models, not necessarily the physical storage. You can have different tables in the same DynamoDB instance for commands and queries.

Mistake

Event sourcing and CQRS are always used together.

Correct

They are independent patterns. You can have event sourcing without CQRS (e.g., replay events to get state) and CQRS without event sourcing (e.g., separate read/write databases in a CRUD system).

Mistake

With event sourcing, you can delete or update events if you make a mistake.

Correct

Events are immutable. To correct a mistake, you append a compensating event (e.g., 'PaymentCorrected'). Deleting or updating breaks the audit trail.

Mistake

Event sourcing guarantees strong consistency for reads.

Correct

Event sourcing typically leads to eventual consistency for read models built from projections. Strong consistency requires additional mechanisms like synchronous updates or strongly consistent reads on the event store.

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 event sourcing and CQRS?

Event sourcing is a pattern for storing state as a sequence of events. CQRS is a pattern that separates commands (writes) from queries (reads). They are independent but often used together: event sourcing provides the event store for the write side, and CQRS defines separate read models built from those events. For the exam, recognize that event sourcing is about how you store data (events), while CQRS is about how you structure your API and data access (separate read/write paths).

Which AWS services can be used as an event store?

Amazon DynamoDB (with append-only writes and streams) and Amazon Kinesis Data Streams are the primary services. DynamoDB is simpler for many use cases and is a common exam answer. Kinesis is better for high-throughput, multi-consumer scenarios. Amazon S3 can store event archives but is not real-time. Amazon RDS is not recommended because it is not append-only by nature and lacks built-in streaming.

How do you implement CQRS on AWS?

Typically, you have a command side (API Gateway + Lambda) that writes events to an event store (DynamoDB). The event store triggers a Lambda via DynamoDB Streams that updates a read-optimized store (e.g., another DynamoDB table, ElastiCache, or Aurora). The query side (API Gateway + Lambda) reads from the read store. Optionally, you can use EventBridge to route events to multiple projections.

What is eventual consistency in the context of CQRS?

Eventual consistency means that after a command is processed, the read model may not reflect the change immediately. There is a delay (usually milliseconds to seconds) while the event is processed and the projection updates. This is acceptable for many applications (e.g., order status). The exam tests that you understand this trade-off: CQRS often sacrifices strong consistency for better read performance and scalability.

How do you handle out-of-order events in event sourcing?

Use a sequence number or timestamp to order events. When processing events, discard or reorder events that arrive out of sequence. For DynamoDB Streams, events within a partition are ordered. For Kinesis, events within a shard are ordered. If you need global ordering, use a single shard or implement a sequencer. The exam may test that you need to handle ordering correctly.

What is a snapshot in event sourcing?

A snapshot is a saved state of an aggregate at a specific point in time. Instead of replaying all events from the beginning, you load the latest snapshot and then replay only events after that snapshot. Snapshots improve performance. They can be stored in the same DynamoDB table with a different sort key (e.g., 'SNAPSHOT#<sequence>'). The exam may ask about when to take snapshots (periodically or after a certain number of events).

Can you use SQS as an event store?

No, SQS is a message queue, not a durable event store. Messages are deleted after processing. SQS is used for decoupling command handling or event processing, but not for long-term storage of events. For event sourcing, use DynamoDB or Kinesis.

Terms Worth Knowing

Ready to put this to the test?

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

Done with this chapter?