Requirements
Functional Requirements
1. Card Operations:
- Insert card
- Eject card
- Read card details
2. Authentication:
- Enter PIN
- Validate PIN with bank
3. Transactions:
- Check balance
- Withdraw cash
- Deposit cash/check
- Transfer between accounts
4. Cash Dispensing:
- Dispense correct denominations
- Handle insufficient funds
- Track cash inventory
5. Receipt:
- Print transaction receipt
- Optional receipt for balance check
Non-Functional Requirements
1. Security: Encrypted PIN, secure communication
2. Reliability: Handle network failures gracefully
3. Availability: 24/7 operation
4. Atomicity: Complete transaction or rollback
ATM States
┌─────────────────────────────────────────────┐
│ ATM States │
├─────────────────────────────────────────────┤
│ │
│ Idle → CardInserted → PINVerified │
│ │ │ │
│ │ ▼ │
│ │ OperationSelected │
│ │ │ │
│ │ ┌────────────┼────────────┐ │
│ │ ▼ ▼ ▼ │
│ │ Balance Withdraw Deposit│
│ │ │ │ │ │
│ │ └────────────┼────────────┘ │
│ │ ▼ │
│ │ TransactionComplete │
│ │ │ │
│ │ ▼ │
│ └──────────────────── CardEjected │
│ │
└─────────────────────────────────────────────┘
State Pattern
ATM State Interface
public interface ATMState {
void insertCard(ATM atm, Card card);
void ejectCard(ATM atm);
void enterPin(ATM atm, String pin);
void selectOperation(ATM atm, OperationType type);
void withdraw(ATM atm, int amount);
void deposit(ATM atm, int amount);
void checkBalance(ATM atm);
}
Idle State
public class IdleState implements ATMState {
@Override
public void insertCard(ATM atm, Card card) {
atm.setCard(card);
System.out.println("Card inserted. Please enter PIN.");
atm.setState(new CardInsertedState());
}
@Override
public void ejectCard(ATM atm) {
System.out.println("No card to eject.");
}
@Override
public void enterPin(ATM atm, String pin) {
System.out.println("Insert card first.");
}
// Other methods throw IllegalStateException
}
CardInserted State
public class CardInsertedState implements ATMState {
@Override
public void enterPin(ATM atm, String pin) {
if (atm.getCard().validatePin(pin)) {
System.out.println("PIN verified. Select operation.");
atm.setState(new AuthenticatedState());
} else {
System.out.println("Invalid PIN. Try again.");
atm.incrementPinAttempts();
if (atm.getPinAttempts() >= 3) {
System.out.println("Card retained.");
atm.ejectCard();
atm.setState(new IdleState());
}
}
}
@Override
public void ejectCard(ATM atm) {
atm.setCard(null);
System.out.println("Card ejected.");
atm.setState(new IdleState());
}
}
Authenticated State
public class AuthenticatedState implements ATMState {
@Override
public void selectOperation(ATM atm, OperationType type) {
switch (type) {
case CHECK_BALANCE:
atm.setState(new BalanceInquiryState());
break;
case WITHDRAW:
atm.setState(new WithdrawState());
break;
case DEPOSIT:
atm.setState(new DepositState());
break;
}
}
}
Withdraw State
public class WithdrawState implements ATMState {
@Override
public void withdraw(ATM atm, int amount) {
// 1. Check balance
if (atm.getCard().getBalance() < amount) {
System.out.println("Insufficient funds.");
return;
}
// 2. Check ATM cash
if (!atm.getCashDispenser().hasEnoughCash(amount)) {
System.out.println("ATM has insufficient cash.");
return;
}
// 3. Dispense cash
atm.getCashDispenser().dispense(amount);
atm.getCard().deduct(amount);
// 4. Print receipt
atm.getReceiptPrinter().printWithdrawal(amount);
// 5. Eject card
atm.ejectCard();
atm.setState(new IdleState());
}
}
Transaction Flow
Complete Withdrawal Flow
┌──────┐ ┌────────┐ ┌──────────┐ ┌──────────┐
│ ATM │ │ Bank │ │ Cash │ │ Receipt │
│ │ │Server │ │Dispenser │ │ Printer │
└──┬───┘ └───┬────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ insertCard │ │ │
│────────────▶│ │ │
│ │ │ │
│ enterPin │ │ │
│────────────▶│ │ │
│ verify │ │ │
│◀────────────│ │ │
│ │ │ │
│ selectWithdraw │ │
│────────────▶│ │ │
│ │ │ │
│ enterAmount │ │ │
│────────────▶│ │ │
│ checkBalance │ │
│◀────────────│ │ │
│ │ │ │
│ │ │ dispenseCash │
│ │ │◀────────────────│
│ │ │ cash dispensed │
│ │ │────────────────▶│
│ │ │ │
│ │ │ │ printReceipt
│ │ │ │◀────────────
│ │ │ │────────────▶
│ │ │ │
│ ejectCard │ │ │
│◀────────────│ │ │
Transaction Recording
public class Transaction {
private final String transactionId;
private final TransactionType type;
private final int amount;
private final LocalDateTime timestamp;
private final TransactionStatus status;
public Transaction(TransactionType type, int amount) {
this.transactionId = UUID.randomUUID().toString();
this.type = type;
this.amount = amount;
this.timestamp = LocalDateTime.now();
this.status = TransactionStatus.PENDING;
}
}
Error Handling
public class TransactionErrorHandler {
public void handle(TransactionError error) {
switch (error.getType()) {
case NETWORK_FAILURE:
// Retry, then rollback
retryOrRollback(error.getTransaction());
break;
case INSUFFICIENT_FUNDS:
// Display message, return to menu
displayMessage("Insufficient funds");
break;
case CASH_DISPENSER_ERROR:
// Rollback, retain card
rollbackTransaction(error.getTransaction());
retainCard();
break;
}
}
}
ATM Class
public class ATM {
private ATMState state;
private Card card;
private final CashDispenser cashDispenser;
private final ReceiptPrinter receiptPrinter;
private final BankService bankService;
public ATM() {
this.state = new IdleState();
this.cashDispenser = new CashDispenser();
this.receiptPrinter = new ReceiptPrinter();
this.bankService = new BankService();
}
public void setState(ATMState state) {
this.state = state;
}
// Delegate all operations to current state
}
Follow-ups
Follow-up Questions
1. How to handle multi-currency?
→ Add Currency enum
→ Exchange rate service
→ Currency selection state
2. How to handle deposits?
→ Deposit state with check/cash scanning
→ Verification state for check clearing
→ Provisional credit
3. How to handle card retention?
→ RetainedCardState
→ Admin retrieval process
→ Notification to bank
4. How to handle ATM cash replenishment?
→ CashReplenishmentService
→ Threshold-based alerts
→ Scheduled replenishment
5. How to handle multi-account cards?
→ Account selection state
→ Different accounts (Savings, Checking)
Design Patterns Used
| Pattern | Usage |
|---|---|
| State | ATM states (Idle, PIN, Auth, etc.) |
| Strategy | Denomination selection |
| Observer | Cash level monitoring |
| Factory | Transaction creation |
| Singleton | ATM instance |
Security Considerations
1. PIN encryption (end-to-end)
2. Card data tokenization
3. Secure communication (TLS)
4. Transaction limits
5. Fraud detection
6. Card retention after failed attempts
Practice Problems
Design a scalable ATM Design system. Cover high-level architecture, data model, and API design.
Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliabilityHow would you scale ATM Design to handle 10x the current load? Identify bottlenecks and solutions.
Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decompositionAnalyze potential failure modes for ATM Design and design mitigation strategies.
Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradationQuiz
1. What pattern is primarily used for ATM behavior?
2. What happens after 3 failed PIN attempts?
3. What must be checked before dispensing cash?
4. What happens during a network failure?
5. Why is the State pattern ideal for ATMs?
Flashcards
Question
What pattern models ATM behavior?
Click to reveal answer
Answer
State pattern. ATM has states: Idle, CardInserted, Authenticated, OperationSelected, etc. Behavior changes per state.
Question
ATM withdrawal flow?
Click to reveal answer
Answer
1) Insert card, 2) Enter PIN, 3) Select withdrawal, 4) Enter amount, 5) Check balance+cash, 6) Dispense, 7) Print receipt, 8) Eject card.
Question
What happens after 3 failed PIN attempts?
Click to reveal answer
Answer
Card is retained by ATM for security. ATM returns to Idle state. Bank is notified.
Question
What must be checked before dispensing cash?
Click to reveal answer
Answer
Card balance must have sufficient funds AND ATM cash inventory must have enough cash in correct denominations.
Question
How to handle ATM network failures?
Click to reveal answer
Answer
Retry logic, then rollback if retry fails. Transaction atomicity: complete or rollback completely.
Revision Notes
Key Takeaways
- 1.ATM uses State pattern to model different states and allowed operations
- 2.PIN verification has a 3-attempt limit before card retention
- 3.Both card balance and ATM cash inventory must be checked before dispensing
- 4.Transactions must be atomic: complete fully or rollback completely
- 5.Error handling covers network failures, insufficient funds, and dispenser errors
Interview Tips
- •Show State pattern with clear state transitions
- •Explain the complete withdrawal flow with all checks
- •Discuss error handling and atomicity
- •Mention security considerations (PIN encryption, card retention)
Cheat Sheet
ATM Design - Cheat Sheet
States:
Idle → CardInserted → Authenticated → OperationSelected → Complete
Key Operations:
- Insert/Eject card
- PIN verification (3 attempts max)
- Balance inquiry
- Withdrawal (check balance + ATM cash)
- Deposit
- Transfer
Withdrawal Flow:
- Validate PIN
- Check card balance
- Check ATM cash
- Dispense cash
- Deduct from account
- Print receipt
- Eject card
Security:
- PIN encryption
- Card retention after failures
- Transaction limits
- Fraud detection
Patterns:
State (ATM states), Strategy (denomination), Observer (cash monitoring)