This project is a demonstration of a distributed system that implements CQRS (Command Query Responsibility Segregation) with Change Data Capture (CDC) and database replication using Docker containers.
This demo showcases how to build a scalable distributed system where:
- Writes go to a single leader database (source of truth)
- Reads are distributed across multiple read replicas for improved performance
- Replication happens automatically through event streaming (CDC)
- Load balancing distributes read queries across replicas using round-robin
Database replication is the process of copying and maintaining database objects (tables, data) in multiple databases to improve:
- Performance: Distribute read queries across multiple replicas
- Availability: If one replica fails, others can still serve requests
- Scalability: Add more replicas to handle increased read load
- Geographic Distribution: Place replicas closer to users
In this system:
- The PostgreSQL Leader is the single source of truth for all writes
- 3 Read Replicas serve read queries, reducing load on the leader
- Changes are captured from the leader using Debezium CDC
- Changes are streamed through Redpanda (Kafka-compatible)
- A Consumer App replicates changes to all read replicas
- The CQRS Write App uses round-robin load balancing to distribute reads across replicas
┌─────────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Write App │───▶│ PostgreSQL │───▶│ Debezium │───▶│ Redpanda │
│ (ASP.NET Core) │ │ (Leader) │ │ Connector │ │ (Event Stream) │
│ │ │ │ │ │ └─────────┬────────┘
│ Reads (Round- │ │ │ │ │ │
│ Robin) ─────────┼───▶│ │ │ │ ▼
└─────────────────┘ └──────────────┘ └──────────────┘ ┌──────────────────┐
│ │ Consumer App │
│ │ (Replicates to │
│ │ Read Replicas) │
│ └─────────┬────────┘
│ │
│ ▼
│ ┌─────────────────┐
└──────────────────────────────────────────────────────▶│ Read │
│ Replicas │
│ (3 instances) │
└─────────────────┘
- PostgreSQL Leader Database - Stores the source of truth for all data writes
- PostgreSQL Read Replicas (3) - Serve read queries for improved performance and scalability
- Debezium Connector - Captures changes from the leader database using CDC
- Redpanda - Event streaming platform (Kafka API compatible) for change events
- CQRS Write Application - ASP.NET Core MVC app that:
- Writes to the leader database
- Reads from replicas using round-robin load balancing
- Includes a Demo page to visualize replica distribution
- Consumer Application - Reads events from Redpanda and replicates changes to all 3 read replicas
- Docker (version 20.10 or later)
- Docker Compose (version 2.0 or later)
Start all Docker containers:
On Windows (PowerShell):
docker-compose up -dOn Linux/Mac:
docker-compose up -dOr use the provided startup scripts:
Windows:
.\start-system.ps1Linux/Mac:
chmod +x start-system.sh
./start-system.shWait approximately 30 seconds for all services to start up and initialize. You can check the status with:
docker-compose psAfter the services are up, register the Debezium connector to start capturing changes from the PostgreSQL leader:
On Windows (PowerShell):
Invoke-WebRequest -Uri "http://localhost:8083/connectors" -Method POST -ContentType "application/json" -InFile ".\debezium\register-postgres.json"On Linux/Mac:
curl -X POST \
http://localhost:8083/connectors \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d @debezium/register-postgres.json- Access the CQRS Write App: Open your browser and navigate to
http://localhost:8080 - Check Debezium Connector: Verify the connector is registered:
curl http://localhost:8083/connectors
- View Demo Page: Navigate to
http://localhost:8080/Home/Demoto see the round-robin load balancing in action
- The CQRS Write Application receives write requests (create, update, delete operations)
- Writes are sent to the PostgreSQL Leader database (single source of truth)
- The leader database stores the changes
- Debezium monitors the PostgreSQL leader using logical replication
- When changes occur, Debezium captures them and sends events to Redpanda
- The Consumer Application reads events from Redpanda
- The consumer replicates the changes to all 3 read replicas simultaneously
- The CQRS Write Application receives read requests
- Reads are distributed across replicas using round-robin load balancing:
- First read → Replica 1
- Second read → Replica 2
- Third read → Replica 3
- Fourth read → Replica 1 (cycles back)
- This distributes the read load evenly across all replicas
The Demo page (/Home/Demo) allows you to:
- Make 1000 sequential read requests
- See which replica handled each request
- View statistics showing the distribution across replicas
- Observe round-robin load balancing in real-time
| Service | Port | Description |
|---|---|---|
| PostgreSQL Leader | localhost:5432 |
Main database for writes |
| PostgreSQL Read Replica 1 | localhost:5433 |
First read replica |
| PostgreSQL Read Replica 2 | localhost:5434 |
Second read replica |
| PostgreSQL Read Replica 3 | localhost:5435 |
Third read replica |
| Redpanda | localhost:19092 |
Kafka-compatible event streaming |
| Debezium Connect | localhost:8083 |
CDC connector REST API |
| CQRS Write App | localhost:8080 |
Web application UI |
docker-compose psView logs for a specific service:
docker-compose logs -f <service-name>For example:
docker-compose logs -f consumer-app
docker-compose logs -f debezium-connect
docker-compose logs -f cqrs-write-appcurl http://localhost:8083/connectors/postgres-connector/statusdocker-compose exec redpanda rpk topic listYou can use Redpanda's console to view topics and messages in a web interface.
To stop all services:
docker-compose downTo stop and remove all volumes (
docker-compose down -v- Ensure Docker and Docker Compose are running
- Check if ports are already in use:
netstat -an | grep <port> - View logs:
docker-compose logs <service-name>
- Verify Redpanda is running:
docker-compose ps redpanda - Check Debezium logs:
docker-compose logs debezium-connect - Ensure the connector was registered successfully
- Check Consumer App logs:
docker-compose logs consumer-app - Verify Redpanda has messages:
docker-compose exec redpanda rpk topic consume <topic-name> - Ensure Debezium is capturing changes from the leader
.
├── cqrs-write-app/ # ASP.NET Core MVC application (write side + reads)
├── consumer-app/ # .NET consumer that replicates to read replicas
├── postgres/
│ ├── init-scripts/ # Leader database initialization
│ └── read-replica-scripts/ # Replica initialization
├── debezium/
│ └── register-postgres.json # Debezium connector configuration
├── redpanda/
│ └── redpanda.yaml # Redpanda configuration
├── docker-compose.yml # All services definition
├── start-system.ps1 # Windows startup script
├── start-system.sh # Linux/Mac startup script
└── README.md # This file
This is a demonstration project for educational purposes.