Core Banking System Design Interview Guide
Design a modern core banking system for system design interviews. Covers requirements, architecture, ledger design, transaction processing, APIs, data model, consistency, security, scalability, observability, and trade-offs.
A core banking system is the source of truth for a bank's customers, accounts, balances, ledger entries, and money movement. In a system design interview, this problem tests whether you can design for correctness, consistency, auditability, security, and high availability.
Unlike social feeds or media systems, a banking system cannot treat data loss or duplicate processing as a minor issue. Money movement must be precise, traceable, reversible through controlled accounting entries, and compliant with financial regulations.
Interview Scope
In an interview, keep the scope clear. A full bank includes cards, loans, trading, fraud, statements, AML, branch operations, mobile apps, and regulatory reporting. For a 45 to 60 minute design interview, focus on the core banking platform:
- Customer and KYC profile management
- Account opening and account lifecycle
- Balance inquiry
- Deposits and withdrawals
- Internal transfers between accounts in the same bank
- External transfer initiation
- Ledger and transaction history
- Notifications and audit trails
Out of scope unless the interviewer asks:
- Credit card authorization network
- Full loan origination workflow
- ATM hardware protocol
- Real-time fraud ML platform
- Investment and trading systems
- Full regulatory reporting engine
Requirements
Functional Requirements
The system should support:
- Create and manage customers
- Complete KYC verification before account activation
- Open checking, savings, and business accounts
- Freeze, close, or reactivate accounts
- Check available and current balance
- Deposit money into an account
- Withdraw money from an account
- Transfer money between two internal accounts
- Initiate external bank transfers
- Generate transaction history and account statements
- Send transaction notifications
- Maintain complete audit logs
Non-Functional Requirements
The system must provide:
- Strong consistency for ledger writes and account balances
- No double debit or double credit for retried requests
- High availability for read operations and channel access
- Low latency for balance inquiry and internal transfers
- Complete auditability for every financial operation
- Encryption in transit and at rest
- Fine-grained authorization for customers, employees, and services
- Disaster recovery with strict RPO and RTO
- Regulatory compliance, including KYC, AML, PCI where applicable, and data retention
Capacity Estimates
Use rough numbers to show structured thinking:
| Metric | Estimate |
|---|---|
| Customers | 50 million |
| Accounts per customer | 2 |
| Total accounts | 100 million |
| Daily balance reads | 300 million |
| Daily transactions | 50 million |
| Peak transaction rate | 5,000 to 10,000 TPS |
| Ledger entries per transaction | 2 or more |
| Daily ledger entries | 100 million plus |
| Retention | 7 to 10 years or more |
Balance reads are much higher than writes. Ledger writes are lower volume but require stronger correctness guarantees.
Core Concepts
Account
An account represents a financial container owned by a customer. It has an account number, product type, status, currency, branch, and balance state.
Ledger
The ledger is an immutable record of debits and credits. It is the most important data structure in the system. Account balances should be derived from or reconciled with ledger entries.
Transaction
A transaction represents a business operation such as deposit, withdrawal, transfer, fee, reversal, or interest credit.
Double-Entry Bookkeeping
Every money movement should create balanced ledger entries. If one account is debited, another account or internal settlement account is credited.
Example internal transfer:
| Entry | Account | Direction | Amount |
|---|---|---|---|
| 1 | Sender account | Debit | 100 |
| 2 | Receiver account | Credit | 100 |
The sum of debits and credits must balance.
High-Level Architecture
flowchart TD
Mobile["Mobile App"] --> Gateway["API Gateway"]
Web["Web Banking"] --> Gateway
Branch["Branch System"] --> Gateway
ATM["ATM Channel"] --> Gateway
Gateway --> Auth["Auth and Authorization Service"]
Gateway --> Customer["Customer Service"]
Gateway --> Account["Account Service"]
Gateway --> Txn["Transaction Service"]
Txn --> Ledger["Ledger Service"]
Txn --> Limit["Limit and Risk Service"]
Txn --> Idem["Idempotency Store"]
Ledger --> LedgerDB[("Ledger Database")]
Account --> AccountDB[("Account Database")]
Customer --> CustomerDB[("Customer Database")]
Ledger --> Events["Event Bus"]
Events --> Notification["Notification Service"]
Events --> Statement["Statement Service"]
Events --> Audit["Audit and Compliance"]
Events --> Reconciliation["Reconciliation Service"]
Txn --> External["External Payment Rails"]
Main Services
API Gateway
Responsibilities:
- Route requests from mobile, web, ATM, and branch channels
- Enforce TLS, request size limits, and rate limits
- Validate authentication tokens
- Add correlation IDs for tracing
Auth and Authorization Service
Responsibilities:
- Authenticate customers and employees
- Enforce MFA for sensitive operations
- Authorize account access
- Support roles such as customer, teller, branch manager, auditor, and admin
Customer Service
Responsibilities:
- Store customer profile
- Track KYC status
- Manage customer risk classification
- Link customers to accounts
Account Service
Responsibilities:
- Open and close accounts
- Store account metadata and status
- Provide balance views
- Enforce account-level restrictions such as freeze, debit block, or credit block
Transaction Service
Responsibilities:
- Validate transfer requests
- Check idempotency key
- Check limits and account status
- Coordinate ledger posting
- Return transaction status
Ledger Service
Responsibilities:
- Create immutable ledger entries
- Enforce double-entry balancing
- Maintain transaction status
- Guarantee atomic debit and credit posting
- Provide ledger history for reconciliation and audit
Notification Service
Responsibilities:
- Send SMS, email, and push notifications
- Consume transaction events asynchronously
- Avoid blocking the main transaction path
Reconciliation Service
Responsibilities:
- Compare ledger, balance snapshots, external payment rails, and settlement files
- Detect mismatches
- Produce exception reports
- Trigger manual or automated repair workflows
Data Model
Customer
| Field | Notes |
|---|---|
| customer_id | Unique customer identifier |
| name | Legal name |
| phone | Verified phone number |
| Verified email | |
| kyc_status | pending, verified, rejected |
| risk_level | low, medium, high |
| created_at | Audit field |
Account
| Field | Notes |
|---|---|
| account_id | Internal unique ID |
| account_number | Customer-facing account number |
| customer_id | Owner |
| account_type | checking, savings, business |
| currency | USD, INR, EUR |
| status | active, frozen, closed |
| current_balance | Posted balance |
| available_balance | Balance available for spending |
| version | Used for optimistic concurrency |
| created_at | Audit field |
Transaction
| Field | Notes |
|---|---|
| transaction_id | Unique transaction ID |
| idempotency_key | Client-provided retry key |
| type | deposit, withdrawal, transfer, reversal |
| status | pending, posted, failed, reversed |
| source_account_id | Nullable for cash deposits |
| destination_account_id | Nullable for withdrawals |
| amount | Decimal, never floating point |
| currency | Transaction currency |
| created_at | Request time |
| posted_at | Ledger posting time |
Ledger Entry
| Field | Notes |
|---|---|
| ledger_entry_id | Unique entry ID |
| transaction_id | Parent transaction |
| account_id | Account affected |
| direction | debit or credit |
| amount | Positive decimal |
| balance_after | Optional but useful for fast statements |
| created_at | Immutable timestamp |
API Design
Create Account
POST /v1/accounts
Authorization: Bearer <token>
Content-Type: application/json
{
"customerId": "cust_123",
"accountType": "CHECKING",
"currency": "USD"
}
Get Balance
GET /v1/accounts/{accountId}/balance
Authorization: Bearer <token>
Response:
{
"accountId": "acc_123",
"currency": "USD",
"currentBalance": "1250.75",
"availableBalance": "1200.75",
"asOf": "2026-07-04T12:30:00Z"
}
Internal Transfer
POST /v1/transfers
Authorization: Bearer <token>
Idempotency-Key: 7f0d8e6a-61f2-4c7a-94cb-12a2d7cbd9d0
Content-Type: application/json
{
"fromAccountId": "acc_sender",
"toAccountId": "acc_receiver",
"amount": "100.00",
"currency": "USD",
"description": "Rent payment"
}
Response:
{
"transactionId": "txn_789",
"status": "POSTED"
}
Internal Transfer Flow
sequenceDiagram
participant Client
participant Gateway
participant Txn as Transaction Service
participant Idem as Idempotency Store
participant Account as Account Service
participant Ledger as Ledger Service
participant Events as Event Bus
Client->>Gateway: POST /v1/transfers
Gateway->>Txn: Forward request
Txn->>Idem: Check idempotency key
Idem-->>Txn: Not processed
Txn->>Account: Validate accounts and status
Account-->>Txn: Accounts valid
Txn->>Ledger: Post debit and credit atomically
Ledger-->>Txn: Transaction posted
Txn->>Idem: Save response for key
Txn->>Events: Publish TransactionPosted
Txn-->>Gateway: POSTED
Gateway-->>Client: 200 OK
Consistency and Transaction Handling
For internal transfers, debit and credit should be committed atomically. The simplest approach is to use a relational database transaction inside the ledger boundary:
- Validate idempotency key.
- Lock accounts in deterministic order to avoid deadlocks.
- Check account status and available balance.
- Insert transaction row.
- Insert debit ledger entry.
- Insert credit ledger entry.
- Update account balances.
- Commit.
- Publish event using the outbox pattern.
Use row-level locking or optimistic concurrency depending on throughput and contention.
For high-contention accounts, such as settlement accounts, partition by account or use a dedicated ledger posting queue.
Idempotency
Banking APIs must handle retries safely. A mobile app, API gateway, or network layer may retry after a timeout. Without idempotency, the same transfer could be posted twice.
Design:
- Require an
Idempotency-Keyfor money movement APIs - Store key, request hash, status, and response
- If the same key is retried with the same request, return the original response
- If the same key is retried with a different request body, reject it
- Expire keys after a safe retention window, such as 24 to 72 hours
Database Choice
Use a relational database for the core ledger because it provides:
- ACID transactions
- Strong constraints
- Row-level locking
- Mature backup and recovery
- Powerful reporting and reconciliation support
Common choices:
- PostgreSQL
- Oracle
- MySQL with strict transaction configuration
Use NoSQL or search stores for secondary workloads:
- Elasticsearch or OpenSearch for transaction search
- Redis for short-lived cache and rate limits
- Data warehouse for analytics
- Object storage for statements and reports
Scaling Strategy
Read Scaling
- Use read replicas for statements and transaction history
- Cache customer profile and account metadata
- Cache balance only with very short TTL or event-driven invalidation
- Use CQRS for heavy statement queries
Write Scaling
- Partition ledger by account ID or account shard
- Keep all entries for one account in the same shard when possible
- Use deterministic account locking
- Use asynchronous processing for notifications, statements, and analytics
- Use outbox events to avoid losing domain events after database commit
Sharding
Sharding options:
| Strategy | Pros | Cons |
|---|---|---|
| By account_id | Good account locality | Cross-account transfers may span shards |
| By customer_id | Easy customer view | Business accounts with many users can become hot |
| By branch/region | Operationally familiar | Uneven load and harder global access |
| By ledger partition | Scales writes | More complex reconciliation |
For interviews, start with a single strongly consistent ledger database, then discuss sharding as scale increases.
External Transfers
External transfers are more complex because another bank or payment network is involved. They usually cannot be completed in one local ACID transaction.
Use a state machine:
stateDiagram-v2
[*] --> Received
Received --> Validated
Validated --> Debited
Debited --> SentToPaymentRail
SentToPaymentRail --> Settled
SentToPaymentRail --> Failed
Failed --> ReversalPosted
Settled --> [*]
ReversalPosted --> [*]
Use saga-style orchestration:
- Debit customer account locally
- Send payment instruction to payment rail
- Wait for settlement or rejection
- Post settlement entry or reversal entry
- Notify customer
Never delete or mutate old ledger entries. Fix financial errors with compensating entries.
Security
Security is a core design requirement, not an add-on.
- Use TLS everywhere
- Encrypt sensitive data at rest
- Tokenize account numbers where possible
- Use MFA for login, beneficiary setup, and high-risk transfers
- Apply least-privilege service-to-service access
- Use HSM or KMS for key management
- Keep complete audit logs for employee actions
- Add device fingerprinting and risk scoring
- Rate limit sensitive endpoints
- Mask account numbers in UI and logs
Observability
Track both technical and financial signals:
- API latency and error rate
- Transaction success and failure rate
- Ledger posting latency
- Idempotency conflict count
- Account lock wait time
- Event publishing lag
- Reconciliation mismatch count
- External rail timeout rate
- Notification delivery rate
Every request should have:
- Correlation ID
- Customer ID or anonymized customer reference
- Transaction ID
- Idempotency key
- Channel ID
Do not log sensitive account numbers, tokens, or personal data in plain text.
Failure Scenarios
| Failure | Handling |
|---|---|
| Client retries transfer after timeout | Return stored idempotent response |
| Notification service is down | Transaction still succeeds; notification retries asynchronously |
| Event bus is down | Use outbox table and publish later |
| Debit succeeds but credit fails | Avoid this with atomic ledger transaction for internal transfers |
| External payment rail times out | Mark pending and reconcile later |
| Read replica is stale | Use primary or strongly consistent read for critical balance |
| Data center failure | Fail over using tested DR plan |
Trade-Offs
Strong Consistency vs Availability
For ledger writes, choose consistency over availability. It is better to reject or delay a transfer than to corrupt balances.
For reads, the system can use replicas and caches, but clearly distinguish between current balance and eventually consistent views.
Microservices vs Modular Monolith
A bank can start with a modular monolith for core ledger logic to keep transactions simple. As scale and teams grow, split services around stable domain boundaries.
Good service boundaries:
- Customer
- Account
- Ledger
- Transaction orchestration
- Notification
- Statement
- Reconciliation
Avoid splitting debit and credit into separate services if it makes atomicity harder.
Derived Balance vs Stored Balance
Computing balance from all ledger entries is correct but slow. Storing balance is fast but must be kept consistent.
Common approach:
- Ledger entries are immutable source of truth
- Account table stores current balance for fast reads
- Reconciliation jobs verify stored balances against ledger entries
Interview Walkthrough
Use this structure in a system design interview:
- Clarify scope: internal transfers, accounts, balance, ledger, statements.
- State key non-functional requirements: consistency, auditability, security, availability.
- Estimate customers, accounts, TPS, and ledger volume.
- Draw the high-level architecture.
- Deep dive into ledger and double-entry bookkeeping.
- Explain internal transfer flow with idempotency.
- Discuss database choice and transaction isolation.
- Cover scaling, sharding, and read replicas.
- Cover failure handling and reconciliation.
- Finish with trade-offs.
Common Interview Mistakes
- Treating balance as a simple number without a ledger
- Using floating point numbers for money
- Ignoring idempotency
- Updating sender and receiver balances in separate transactions
- Allowing services to directly write each other's databases
- Not discussing audit logs
- Not separating internal transfers from external payment rails
- Overusing async processing for operations that require atomic consistency
- Ignoring account locks, frozen accounts, and limits
Final Design Summary
A strong core banking design should use:
- API Gateway for all banking channels
- Auth service with MFA and authorization
- Customer service for profile and KYC
- Account service for account lifecycle and balance view
- Transaction service for orchestration
- Ledger service as the source of truth
- Relational database for ACID ledger writes
- Idempotency store for safe retries
- Outbox pattern for reliable events
- Event bus for notifications, statements, audit, and reconciliation
- Reconciliation jobs for financial correctness
- Strict security, observability, and disaster recovery controls
The most important interview point is this: in banking, correctness beats convenience. Design the ledger first, then build channels, APIs, caches, and analytics around it.