Whenever you are using any Banking App, What are the basic operations performed :
- Check Account Balance (ofc; While doing any type of transaction)
- Transfer Money (eg: Shopping)
- Transition history
At first, it's look simple. But behind the scenes, you design a typical CRUD-based system:
- One database
- One model for everything (read + write) And it works… until it doesn’t.
The
The Core Problem in Banking Systems
Banking systems must handle two very different workloads:
Writes (Commands)
- Transfer money
- Deposit cash
- Withdraw funds
Writes must be 100% consistent, Failure-safe and Auditable
Reads (Queries)
- Check account balance
- View transactions
- Generate statements
Reads must be Fast, Scalable and Available anytime
If both run on the same system Heavy reads slow down critical transactions, Writes block reads and Risk of inconsistent or delayed updates.
To solve this Bank uses CQRS.
What is CQRS?
Instead of using a single model for both reading and writing (like in traditional CRUD systems), CQRS splits them:
Command side (Write) → Handles updates (create, update, delete)
Query side (Read) → Handles data retrieval
Step-by-Step System Design
- Command Side (Write Model) Handles money movement Example: Transfer ₹5000
Flow:
- User initiates transfer
- System validates:
- Sufficient balance
- Fraud checks
- Deduct from Account A
- ➕ Add to Account B
- Store transaction record
- Query Side (Read Model) Handles user-facing views
- Show balance
- Show transaction history
- Monthly statements
In a CQRS-based banking system, the data flow starts when a user initiates an action like transferring money. This request is treated as a command and is sent to the command service, which is responsible for handling all write operations. The command service performs necessary validations such as checking account balance, verifying security constraints, and ensuring the transaction is legitimate. Once validated, the system updates the write database—deducting money from the sender’s account and adding it to the receiver’s account—while also recording the transaction in a reliable, consistent ledger.
After the write operation is successfully completed, the system emits an event, such as “MoneyTransferred.” This event is published to a messaging system (like Kafka or RabbitMQ), which acts as a bridge between the write side and the read side. The query (read) service listens to these events and updates the read database accordingly. This read database is structured for fast access and may store precomputed balances and transaction summaries.
When the user later checks their account balance or transaction history, the request goes to the query service instead of the write system. The query service retrieves data from the optimized read database and returns it quickly. Because the read model is updated asynchronously through events, there might be a very short delay before the latest transaction is reflected, which is known as eventual consistency. However, the write system always remains the source of truth, ensuring that all financial operations are accurate and secure.
CQRS allows systems to handle higher traffic efficiently, improves performance, and simplifies scaling by allows independent optimization of read and writes parts.


Top comments (0)