DEV Community

Deepika Pusala
Deepika Pusala

Posted on

CQRS & Event-Driven Thinking: Commands, Queries, Domain Events & the Outbox Pattern Explained ๐Ÿ“จ

๐Ÿ“จ CQRS & Event-Driven Thinking: Let's Learn from ZERO!

These are important backend architecture concepts, especially with FastAPI, PostgreSQL, SQLAlchemy, repositories, services and a social media app. We'll cover:

  • ๐Ÿ”€ What Command-Query Separation (CQS) means
  • ๐Ÿงฉ What CQRS is and how it differs from CQS
  • โš”๏ธ Commands vs queries
  • ๐Ÿ“ฃ What domain events are
  • ๐ŸŒ How event-driven architecture works
  • ๐Ÿข Real examples using an Instagram-like app
  • โœ… Advantages, disadvantages, use cases and interview questions

We'll start simple and gradually move towards real backend code. ๐Ÿš€


1. ๐Ÿค” First, Understand the Problem

You're building an Instagram-like app. A user can:

  1. Create a post
  2. Like a post
  3. Comment on a post
  4. Follow another user
  5. View their home feed
  6. Search for posts
  7. View someone's profile

All of these touch the database. But are they all doing the same kind of work? No! There are two fundamentally different kinds of operations:

Kind Examples Effect
โœ๏ธ Writing / changing data Create a post, like a post, delete a comment, follow a user Changes the database
๐Ÿ‘€ Reading / retrieving data Get all posts, view comments, get a profile, search posts Retrieves information without changing it

This distinction is the foundation of Command-Query Separation.


2. ๐Ÿ”€ Command-Query Separation (CQS)

  • Command: an instruction to perform an action
  • Query: a request to retrieve information
  • Separation: keeping these two responsibilities apart

๐Ÿ“Œ Command-Query Separation means separating operations that change data from operations that only read data. A command changes the state of the system. A query returns information but does not change the state.

State means the current stored condition of your application. For example, a post:

{
  "id": 101,
  "caption": "My first post",
  "likes_count": 10
}
Enter fullscreen mode Exit fullscreen mode

Its current state: the post has 10 likes.

2.1 โœ๏ธ Command example: like a post

The frontend sends POST /posts/101/like. The backend:

  1. Identifies the logged-in user
  2. Checks the post exists
  3. Checks the user hasn't already liked it
  4. Inserts a like record
  5. Updates the like count, if your design stores one
Before:  { "id": 101, "likes_count": 10 }
After:   { "id": 101, "likes_count": 11 }
Enter fullscreen mode Exit fullscreen mode

The database state changed, so this is a Command.

2.2 ๐Ÿ‘€ Query example: get post details

The frontend sends GET /posts/101. The backend finds the post, retrieves its caption, author and like count, and returns:

{
  "id": 101,
  "caption": "My first post",
  "likes_count": 11
}
Enter fullscreen mode Exit fullscreen mode

Did we change anything? No. So this is a Query.

2.3 ๐Ÿ“Š Visual comparison

โœ๏ธ COMMAND (write) ๐Ÿ‘€ QUERY (read)
Changes application state Does not change application state
Create, Update, Delete, Like Get, List, Search, Retrieve

๐Ÿง  Command = change something. Query = ask for something.

2.4 ๐Ÿ“‹ More examples

Operation Type Reason
Create post Command Inserts data
Edit caption Command Updates data
Delete post Command Removes data
Like post Command Changes like state
Follow user Command Changes follow relationship
Get profile Query Reads data
Get feed Query Retrieves posts
Search users Query Reads matching records
Get comments Query Retrieves comments

๐Ÿ“ A query can have internal work such as logging or caching, but its purpose must not be to change the business data being queried.


3. ๐Ÿคท Why Do We Need CQS?

Imagine this function:

def get_post(post_id):
    post = database.get_post(post_id)

    if post.likes_count == 0:
        post.likes_count = 1
        database.save(post)

    return post
Enter fullscreen mode Exit fullscreen mode

What's wrong? It's named get_post(), so we expect it to retrieve a post. But it also changes the database! Someone calling get_post() might accidentally modify application data. A better design:

def get_post(post_id):
    return database.get_post(post_id)


def like_post(post_id):
    post = database.get_post(post_id)
    post.likes_count += 1
    database.save(post)
    return post
Enter fullscreen mode Exit fullscreen mode
  • get_post() โ†’ Query
  • like_post() โ†’ Command

โœ… Main benefit: your code becomes easier to understand, maintain, test and debug.


4. ๐Ÿงฉ What is CQRS?

๐Ÿ“Œ CQRS = Command Query Responsibility Segregation. It's an architectural pattern based on the same idea as CQS.

  • CQS is a principle about separating read and write operations
  • CQRS takes that further by letting the read and write sides have different models, responsibilities, and sometimes different data stores

4.1 ๐Ÿ›๏ธ Traditional architecture

Frontend (React)
     โ–ผ
Backend API (FastAPI)
     โ–ผ
PostgreSQL Database   โ† same database for reading and writing
Enter fullscreen mode Exit fullscreen mode

Writing means insert/update/delete, reading means select. This is completely valid and works very well for many apps. But if your app becomes extremely popular (1 million users, 10 million posts, millions of feed requests, thousands of new posts and likes every minute) the database must handle heavy reads and writes together. That's where CQRS can become useful.

4.2 ๐Ÿงฑ CQRS architecture

                    Frontend (React)
                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              Command            Query
          (write request)    (read request)
                 โ–ผ                  โ–ผ
         Command Handler      Query Handler
   (validates and processes   (processes read
            writes)              requests)
                 โ–ผ                  โ–ผ
          Write Model          Read Model
          (PostgreSQL)    (optimized for retrieval)
Enter fullscreen mode Exit fullscreen mode

(Conceptual. Read and write models may use the same database or different ones.)

Write side: handles commands, validates business rules, changes application state, saves data.

Read side: handles queries, retrieves information, returns data in a format suitable for the frontend, and can be optimized for fast reads.

4.3 ๐Ÿ—„๏ธ Does CQRS require two databases?

No! A very important interview question. There are two common implementations:

Option A: same database, separate models. Command and query logic are separate, but they share the same underlying database. For example, the command handler uses the SQLAlchemy ORM to create a post, while the query handler uses optimized SQL to retrieve posts.

Option B: separate read and write databases. Write model: PostgreSQL. Read model: Redis, Elasticsearch, or a separate PostgreSQL database. The read database may hold precomputed or denormalized information for faster retrieval. Use this when you need independent scaling or specialized read storage.

4.4 ๐Ÿ“– What is a Read Model?

๐Ÿ“Œ A read model is a representation of data designed specifically for reading.

Suppose your database has:

users:

id username
1 deepika
2 arjun

posts:

id user_id caption
101 1 My first post
102 2 Weekend vibes

likes:

id post_id user_id
1 101 2
2 101 1

To display a feed you may need to join several tables and count likes. Your frontend wants:

[
  {
    "post_id": 101,
    "username": "deepika",
    "caption": "My first post",
    "likes_count": 2
  }
]
Enter fullscreen mode Exit fullscreen mode

This combined structure is a read model. It's shaped for the frontend's needs rather than matching your original tables. Instead of repeating expensive joins and calculations on every feed request, you can maintain a read-optimized representation, for example a precomputed like count or a denormalized feed projection.

4.5 โœ๏ธ What is a Write Model?

The write model is the representation used to perform and enforce changes. When a user likes a post, the write side must ensure the post exists, the user exists, the user hasn't already liked it, and the like is stored correctly. It maintains business rules and data integrity.

Write Model Read Model
Handles changes Handles retrieval
Processes commands Processes queries
Enforces business rules Optimizes presentation
Focuses on consistency Focuses on efficient reads

4.6 ๐Ÿ“ When should we use CQRS?

  1. Read-heavy apps: millions of users frequently retrieve information
  2. Complex queries: many joins, aggregations and calculations
  3. Different scaling needs: read and write traffic scale independently
  4. Different data representations: the frontend needs data structured differently from the normalized write database
  5. Complex business workflows: many operations and rules affect the same domain

When should you avoid it? For a small CRUD app, CQRS adds unnecessary complexity. You'd maintain two models, command handlers, query handlers, event processing, synchronization, and error handling for failed projections. A normal layered FastAPI app is often enough.

โš ๏ธ CQRS is an architectural choice, not a requirement for every backend.


5. ๐Ÿ“ฃ What is Event-Driven Thinking?

A user creates a post and the backend saves it. After that, several other things may need to happen:

  1. The post should appear in the user's feed
  2. Followers may receive notifications
  3. The profile's post count may increase
  4. Analytics may record the new post
  5. An image-processing service may generate thumbnails

Should the post-creation function directly perform all of these? It could, but if the notification service is slow or temporarily unavailable, should the user wait for it before their request finishes? Probably not. That's where event-driven thinking helps.

5.1 ๐Ÿ“Œ What is an Event?

๐Ÿ“Œ An event represents something that has already happened.

Examples: UserRegistered, PostCreated, PostLiked, CommentAdded, UserFollowed, OrderPlaced, PaymentCompleted.

Notice the past tense. An event is a completed fact. PostCreated means a post has already been created. It doesn't mean someone is requesting creation. That distinction is extremely important.

5.2 โš”๏ธ Command vs Event

โœ๏ธ Command ๐Ÿ“ฃ Event
CreatePost: "Please create a new post." PostCreated: "A new post has been successfully created."
An instruction that requests something happen A fact that announces something happened
Command Event
RegisterUser UserRegistered
CreatePost PostCreated
LikePost PostLiked
AddComment CommentAdded
FollowUser UserFollowed
PlaceOrder OrderPlaced
MakePayment PaymentCompleted

๐Ÿง  Command = request to do something. Event = notification that something has happened.

5.3 ๐ŸŒ How does event-driven architecture work?

1. User creates a post          โ†’ React frontend
        โ–ผ
2. CreatePost command           โ†’ backend receives the instruction
        โ–ผ
3. Command Handler              โ†’ validates and saves the post
        โ–ผ
4. PostCreated event            โ†’ a fact is published after successful creation
        โ–ผ
5. Event Bus / Event Broker     โ†’ distributes the event to interested consumers
        โ”œโ”€โ”€ ๐Ÿ”” Notifications
        โ”œโ”€โ”€ ๐Ÿ–ผ๏ธ Media
        โ””โ”€โ”€ ๐Ÿ“Š Analytics
Enter fullscreen mode Exit fullscreen mode

One event can trigger several independent reactions.

A. Event Producer: the component that creates and publishes an event. Here, the post creation service saves the post and publishes PostCreated.

B. Event: carries information about what happened:

{
  "event_id": "evt_001",
  "event_type": "PostCreated",
  "post_id": 101,
  "user_id": 1,
  "created_at": "2026-11-12T10:30:00Z"
}
Enter fullscreen mode Exit fullscreen mode

(An illustrative payload.) It tells other components which event occurred, which post was created, which user created it and when. An event should contain enough information for consumers to do their work, without unnecessarily exposing private data.

C. Event Bus / Event Broker: a communication system that receives events from producers and delivers them to interested consumers. Technologies include RabbitMQ, Apache Kafka, Redis Streams, AWS EventBridge. For a small app you can even use a simple in-process event dispatcher with no broker at all.

D. Event Consumer: a component that receives and reacts to an event. When PostCreated occurs, the notification consumer sends notifications, the analytics consumer records the activity, and the media consumer processes images. Each has its own responsibility.

๐Ÿ”— The event producer doesn't need to know the internal implementation of every consumer, which reduces coupling.


6. ๐Ÿท๏ธ What are Domain Events?

๐Ÿ“Œ A domain event is an event that represents something meaningful that happened in the application's business domain.

A domain is the business area or problem your app is built to handle:

Application Domain
Instagram-like app Social networking
Banking app Banking
E-commerce app Online shopping
Hospital app Healthcare management
Food delivery app Food ordering and delivery

In a social media app the domain includes users, posts, comments, likes, follows and notifications. When a user follows another user and the operation succeeds, we can represent that important business fact as the domain event UserFollowed.

6.1 ๐Ÿ“‹ Examples of domain events

Social media:

Domain Event Meaning
UserRegistered A new account was registered
ProfileUpdated A profile was updated
PostCreated A new post was created
PostDeleted A post was deleted
PostLiked A user liked a post
CommentAdded A comment was added
UserFollowed A user followed another user
UserUnfollowed A user unfollowed another user

E-commerce:

Domain Event Meaning
ProductAddedToCart A product was added
OrderPlaced An order was placed
PaymentCompleted Payment succeeded
OrderCancelled An order was cancelled
ItemShipped An item was shipped

6.2 โš–๏ธ Domain event vs command vs query

Concept Meaning Example
Command Request to perform an action LikePost
Query Request to retrieve data GetPost
Domain Event Something meaningful happened PostLiked

A complete sequence:

  1. A user clicks Like
  2. The backend receives LikePost
  3. The backend checks the rules
  4. The backend saves the like
  5. The backend generates PostLiked
  6. Other components react to PostLiked
LikePost (Command)
     โ–ผ
Business logic + database write
     โ–ผ
PostLiked (Domain Event)
     โ–ผ
Event consumers: ๐Ÿ”” Notification ยท ๐Ÿ“Š Analytics
Enter fullscreen mode Exit fullscreen mode

The command asks the system to do something. The event records that the system successfully did it.

6.3 ๐Ÿค” Are domain events always published to Kafka or RabbitMQ?

No. A domain event is a concept. It can exist entirely inside your application. For example, your Python app can create a PostCreated event object and pass it to an internal event dispatcher. You only need Kafka, RabbitMQ or another broker when your architecture requires the capabilities they provide.


7. โณ Synchronous vs Asynchronous Event Processing

7.1 ๐Ÿข Synchronous processing

The caller waits for the operation to finish before continuing:

def create_post(post_data):
    post = save_post(post_data)
    send_notification(post)
    update_analytics(post)
    return post
Enter fullscreen mode Exit fullscreen mode
  1. Save post
  2. Wait for notification
  3. Wait for analytics
  4. Return response

If notification takes 3 seconds and analytics takes 2, the request may take about 5 seconds longer than the database save itself. The user waits for all of it.

7.2 ๐Ÿ‡ Asynchronous event processing

def create_post(post_data):
    post = save_post(post_data)

    publish_event(
        "PostCreated",
        {"post_id": post.id},
    )

    return post
Enter fullscreen mode Exit fullscreen mode

Event consumers process their tasks independently (notifications, analytics, image processing), and the request doesn't have to wait for them.

Synchronous:   Save post โ†’ Send notification โ†’ Update analytics โ†’ Return response (after all)

Asynchronous:  Save post โ†’ Publish event โ†’ Return response
                              โ”œโ”€โ”€ Notification
                              โ””โ”€โ”€ Analytics   (processed independently)
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ Asynchronous doesn't automatically mean a separate server or message broker. Python background tasks, queues and distributed brokers are different implementation options. For reliable processing across services, a durable message broker is often preferable to an in-memory background task.

7.3 ๐Ÿ• What is Eventual Consistency?

A user creates a post. The write database saves it immediately, but your separate read database or feed projection may take a moment to receive and process PostCreated. For a brief moment the write database says the post exists, while the read database hasn't caught up. After the event is processed, both agree.

Time T1 (write completed)   Write DB: Post exists (ID 101)   Read DB: Post not yet visible
Time T2 (event processed)   Write DB: Post exists (ID 101)   Read DB: Post exists (ID 101)
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Œ Eventual consistency: the system may temporarily show slightly outdated information, but if processing succeeds and continues, the read side eventually catches up.

For analytics, recommendation updates and notification delivery, a small delay is often acceptable. But for critical operations such as checking an account balance before a withdrawal, you generally need strong consistency at the point of decision.


8. ๐Ÿ’ฅ What Happens If Event Processing Fails?

Your app saves a post in PostgreSQL, then tries to publish PostCreated to RabbitMQ, but RabbitMQ is temporarily down. The post exists, yet the event was never published, so the notification service may never learn about it. That's a reliability problem. One important solution is the Transactional Outbox Pattern.

8.1 ๐Ÿ“ค Transactional Outbox Pattern

Instead of saving business data and publishing the event as two unrelated operations, save both the business data and an event record in the same database transaction.

posts:

id caption
101 My first post

outbox_events:

id event_type payload published
1 PostCreated post_id:101 false

The process:

  1. Start a database transaction
  2. Insert the post
  3. Insert the PostCreated event into the outbox table
  4. Commit the transaction
  5. A background worker reads unpublished events
  6. The worker publishes them to the broker
  7. The worker marks them as published after successful delivery
BEGIN TRANSACTION
  Insert post
  Insert event
COMMIT              โ† both records saved together
     โ–ผ
Background Worker
     โ–ผ
Message Broker
Enter fullscreen mode Exit fullscreen mode

Even if the broker is down, the event remains safely stored in PostgreSQL and can be published later.

โš ๏ธ Caveat: a worker can publish an event and crash before marking it as published, so the event may be delivered again. That's why consumers should be idempotent.

8.2 ๐Ÿ” What is Idempotency?

๐Ÿ“Œ Idempotency means performing the same operation multiple times produces the same intended result as performing it once.

Suppose PostLiked is delivered twice. If your consumer blindly increments the like count each time, the count goes up twice. That's wrong. A safer design:

  • Store processed event IDs
  • Check whether an event was already processed
  • If yes, skip it
  • If not, process it and record its ID

9. โšก How Does This Fit into FastAPI Architecture?

9.1 ๐Ÿฅž Traditional layered architecture

For POST /posts:

frontend โ†’ FastAPI Router โ†’ Post Service โ†’ Post Repository โ†’ PostgreSQL
Enter fullscreen mode Exit fullscreen mode

The router receives the request, the service implements business logic, and the repository handles database operations.

9.2 ๐Ÿงฉ Introducing CQRS

Separate the write and read responsibilities:

                     FastAPI
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        Command Router       Query Router
              โ–ผ                   โ–ผ
       Command Handler       Query Handler
              โ–ผ                   โ–ผ
       Write Repository      Read Repository
              โ–ผ                   โ–ผ
          Database            Database
Enter fullscreen mode Exit fullscreen mode

Command handler:

class CreatePostCommand:
    def __init__(self, user_id, caption):
        self.user_id = user_id
        self.caption = caption


def handle_create_post(command, repository):
    post = repository.create(
        user_id=command.user_id,
        caption=command.caption,
    )

    return post
Enter fullscreen mode Exit fullscreen mode

Query handler:

def handle_get_post(post_id, repository):
    return repository.get_by_id(post_id)
Enter fullscreen mode Exit fullscreen mode

(Simplified examples, not a complete production implementation.)

  • CreatePostCommand represents the instruction
  • handle_create_post() processes the write
  • handle_get_post() retrieves information

You can have separate command and query handlers even when both use the same SQLAlchemy session and PostgreSQL database.

9.3 ๐Ÿ“ฃ Introducing domain events

Extend the command handler:

def handle_create_post(command, repository, event_publisher):
    post = repository.create(
        user_id=command.user_id,
        caption=command.caption,
    )

    event = {
        "event_type": "PostCreated",
        "post_id": post.id,
        "user_id": command.user_id,
    }

    event_publisher.publish(event)

    return post
Enter fullscreen mode Exit fullscreen mode
CreatePost Command
     โ–ผ
Command Handler
     โ–ผ
Post Repository
     โ–ผ
PostgreSQL
     โ–ผ
PostCreated Event
     โ–ผ
Event Publisher
     โ–ผ
Event Consumers
Enter fullscreen mode Exit fullscreen mode

โš ๏ธ For production reliability, you'd normally use the transactional outbox pattern instead of relying on a direct database-write-then-publish sequence.


10. ๐Ÿ†š Domain Events vs Integration Events

Related, but not identical.

Domain event: represents an important business fact inside a particular domain. It might be used by your app's internal business logic.

PostCreated(
    post_id=101,
    user_id=1,
)
Enter fullscreen mode Exit fullscreen mode

Integration event: communicates something that happened to another system or independent service. It might be published through RabbitMQ or Kafka so another service can consume it.

{
  "event_type": "PostCreated",
  "post_id": 101,
  "user_id": 1
}
Enter fullscreen mode Exit fullscreen mode
Domain Event Integration Event
Internal to a domain Shared across service boundaries
Represents business facts Communicates business facts to other systems
Can be an internal Python object Usually has a serialized message format
May never leave the application Intended for external consumers

A domain event can lead to an integration event, but not every domain event needs to be published externally.


11. โš”๏ธ CQRS vs Event-Driven Architecture vs Event Sourcing

These three are often discussed together but solve different problems.

๐Ÿ…ฐ๏ธ CQRS. Separate reading and writing responsibilities. CreatePost โ†’ command, GetPost โ†’ query. Focus: how your app handles reads and writes.

๐Ÿ…ฑ๏ธ Event-Driven Architecture (EDA). Components communicate and react to events. PostCreated triggers notifications, analytics and image processing. Focus: how components communicate and respond to changes.

๐Ÿ…ฒ Event Sourcing. A different concept. Don't confuse it with EDA. Normally your database stores the current state:

{
  "account_id": 1,
  "balance": 700
}
Enter fullscreen mode Exit fullscreen mode

With event sourcing, instead of treating current state as the primary record, you store the sequence of events that produced it:

Event Amount
AccountCreated 0
MoneyDeposited +1000
MoneyWithdrawn -200
MoneyWithdrawn -100

The balance is calculated by replaying the events: 1000 โˆ’ 200 โˆ’ 100 = 700.

Traditional database:  Current balance = โ‚น700
Event sourcing:        Deposited โ‚น1,000 โ†’ Withdrawn โ‚น200 โ†’ Withdrawn โ‚น100 โ†’ Current balance = โ‚น700
Enter fullscreen mode Exit fullscreen mode

Why use it? Complete history of changes, ability to reconstruct previous states, audit trails, and understanding exactly how a value changed over time.

But it adds complexity: event versioning, event replay, data migration, handling duplicate events and maintaining projections. You don't need event sourcing just because you're using events.

Concept Main responsibility
CQS Separate reads and writes at the operation level
CQRS Separate read and write models/responsibilities
Event-Driven Architecture Communicate through events
Domain Events Represent important business facts
Event Sourcing Store state changes as an event history

๐ŸŽฏ Very important: CQRS and EDA can be used together, but neither requires Event Sourcing.


12. ๐Ÿงต Practical Example: A Social Media App

Imagine Deepika likes Arjun's post.

Step 1: the frontend sends a command

POST /posts/101/like
Authorization: Bearer access_token
Enter fullscreen mode Exit fullscreen mode

Step 2: FastAPI receives the request

@router.post("/posts/{post_id}/like")
async def like_post(
    post_id: int,
    current_user=Depends(get_current_user),
):
    return await service.like_post(
        post_id,
        current_user.id,
    )
Enter fullscreen mode Exit fullscreen mode

Step 3: the service processes the command. It checks the post exists, checks whether the user already liked it, applies business rules, and creates the like record.

Step 4: the database saves the change

likes
id | post_id | user_id
1  | 101     | 1
Enter fullscreen mode Exit fullscreen mode

Step 5: a domain event is generated

{
  "event_type": "PostLiked",
  "post_id": 101,
  "user_id": 1
}
Enter fullscreen mode Exit fullscreen mode

Step 6: event consumers react. The notification service informs the post owner, analytics records the interaction, and a read projection updates the like count, if the app maintains one.

Step 7: the user retrieves the post

GET /posts/101
Enter fullscreen mode Exit fullscreen mode

This is a Query. The query handler returns:

{
  "post_id": 101,
  "caption": "My first post",
  "likes_count": 15,
  "liked_by_me": true
}
Enter fullscreen mode Exit fullscreen mode

This one example demonstrates CQS, CQRS, domain events, event-driven architecture, event consumers and eventual consistency.

๐Ÿ“ If liked_by_me must update immediately, read it from the authoritative write-side data, or otherwise ensure the response reflects the successful write. Don't assume an asynchronously updated read model is immediately current.


13. ๐Ÿ“– Important Terms

Term Beginner-friendly meaning
Command Handler Processes a command
Query Handler Processes a query
Write Model Handles data modifications
Read Model Represents data for retrieval
Event Producer Creates and publishes events
Event Consumer Receives and processes events
Event Bus Distributes events
Event Broker Messaging infrastructure for event delivery
Domain Event Important business occurrence
Event Payload Data contained in an event
Eventual Consistency Different data representations become consistent over time
Idempotency Repeating an operation doesn't produce duplicate effects
Event Sourcing Stores events as the primary history of state changes
Transactional Outbox Reliably stores events alongside database changes
Message Queue Holds messages until consumers process them

14. ๐Ÿงญ When Do These Concepts Work Together?

Situation Useful concept
Separating get_post() and create_post() CQS
Different read and write models CQRS
Sending notifications after a post is created Event-driven architecture
Representing a successful follow action Domain events
Updating a read database asynchronously CQRS + events
Keeping event publication reliable Transactional outbox
Preventing duplicate message processing Idempotency
Keeping complete historical state changes Event sourcing

15. ๐ŸŽค Interview Questions and Answers

Q1. What is Command-Query Separation?
A design principle that separates operations into Commands, which modify application state, and Queries, which retrieve information without modifying business state.

Q2. What is CQRS?
Command Query Responsibility Segregation: an architectural pattern that separates read and write responsibilities, potentially using different models and data stores.

Q3. What's the difference between CQS and CQRS?
CQS is a principle applied at the operation or method level. CQRS is an architectural pattern that applies the separation more broadly to the application's read and write models.

Q4. What is a Domain Event?
Something important that has already happened in the business domain, such as PostCreated, PaymentCompleted or UserRegistered.

Q5. What's the difference between a Command and an Event?
A Command requests an action. An Event represents a fact that the action has happened.

Q6. What is Event-Driven Architecture?
An approach in which components communicate by publishing and consuming events. It helps decouple components and allows independent processing.

Q7. What is an Event Broker?
Infrastructure that receives and distributes messages between producers and consumers, such as RabbitMQ and Kafka.

Q8. What is Eventual Consistency?
Different data representations may temporarily contain different information but eventually become consistent after updates are processed.

Q9. What is the Transactional Outbox Pattern?
A reliability pattern that stores business data and the corresponding event record in the same database transaction. A background worker later publishes the event.

Q10. Does CQRS require two databases?
No. CQRS can use the same database with separate read and write models. Separate databases are optional.

Q11. Is Event-Driven Architecture the same as Event Sourcing?
No. EDA uses events for communication between components. Event sourcing stores events as the primary record of state changes.

Q12. What are the disadvantages of CQRS?
Architectural complexity, additional code, synchronization challenges, potential eventual consistency, and more difficult debugging.


16. ๐Ÿงช Test Your Understanding!

Try these before peeking at the answers! โค๏ธ

  1. A user requests their profile details. What is this?

    • A) Command
    • B) Query
    • C) Domain Event
  2. A user clicks the Like button. What is the incoming instruction?

    • A) PostLiked
    • B) LikePost
    • C) GetPost
  3. The backend successfully saves a post. What event can represent this?

    • A) CreatePost
    • B) GetPost
    • C) PostCreated
  4. A post creation event triggers notifications and analytics independently. Which architecture is this?

    • A) Event-Driven Architecture
    • B) Event Sourcing
    • C) Normalization
  5. Does CQRS always require two databases?

    • A) Yes
    • B) No
  6. What helps reliably publish events after a database transaction?

    • A) B-tree index
    • B) Transactional Outbox
    • C) JWT
  7. What does Eventual Consistency mean?

    • A) Data never changes
    • B) All systems update at exactly the same time
    • C) Data representations may temporarily differ but eventually catch up

๐Ÿ‘€ Click to reveal answers
  1. B: Query. It reads data without changing it
  2. B: LikePost is the command. PostLiked is the event that follows
  3. C: PostCreated. Events are past-tense facts
  4. A: Event-Driven Architecture
  5. B: No. The same database with separate models is a valid CQRS implementation
  6. B: Transactional Outbox
  7. C: Representations may temporarily differ but eventually catch up


17. ๐Ÿ Remember These Five Things

  1. ๐Ÿ”€ CQS = separate reading and writing operations
  2. ๐Ÿงฉ CQRS = separate read and write responsibilities/models
  3. โœ๏ธ Command = an instruction to perform an action
  4. ๐Ÿ“ฃ Domain Event = something meaningful that happened
  5. ๐ŸŒ EDA = components react to events and communicate through them

If this post helped you, drop a โค๏ธ or ๐Ÿฆ„ and share it with a friend learning backend architecture. Happy coding! ๐Ÿ‘ฉโ€๐Ÿ’ปโœจ

Top comments (0)