ATM Machine Design
ATM LLD models card authentication, PIN validation, cash dispensing, balance inquiry, and transaction logging — often coordinated with a remote BankHost abstraction.
Introduction
ATM LLD models card authentication, PIN validation, cash dispensing, balance inquiry, and transaction logging — often coordinated with a remote BankHost abstraction.
Split hardware interfaces (CardReader, CashDispenser, Printer) from AtmService orchestration. State diagram for session: idle → authenticated → transaction → idle.
Discuss rollback when dispense fails after debit.
Understanding the topic
Key concepts
- Components: ATM, CardReader, CashDispenser, Keypad, Screen, BankHost.
- Session after successful PIN.
- Operations: withdraw, deposit, balance, transfer.
- Transaction log for audit.
- Hardware failure compensation (rollback credit).
- Daily withdrawal limits on account.
classDiagramclass ATM {+withdraw(amount)+deposit(amount)}class CashDispenserclass CardReaderATM --> CashDispenserATM --> CardReader
Step-by-step explanation
- Insert card; CardReader reads token.
- Validate PIN with BankHost.
- User selects operation and amount.
- AtmService debits/credits via BankHost.
- CashDispenser dispenses on withdraw.
- Print receipt; eject card; end session.
Informative example
ATM with hardware abstractions and compensating transaction on dispense failure:
public interface BankHost {Optional<Account> authenticate(String cardToken, String pin);boolean debit(Account account, Money amount);void credit(Account account, Money amount);}public interface CashDispenser {boolean dispense(Money amount);}public final class AtmService {private final BankHost bank;private final CashDispenser dispenser;private final List<Transaction> log = new ArrayList<>();public AtmService(BankHost bank, CashDispenser dispenser) {this.bank = bank;this.dispenser = dispenser;}public Result withdraw(Account account, Money amount) {if (amount.isZero()) return Result.failed("invalid amount");if (!bank.debit(account, amount)) return Result.failed("insufficient funds");if (!dispenser.dispense(amount)) {bank.credit(account, amount);return Result.failed("dispenser jam");}log.add(Transaction.withdraw(account.id(), amount));return Result.ok();}}public record Account(String id, Money balance) {}public record Transaction(String accountId, Money amount, Instant at) {static Transaction withdraw(String accountId, Money amount) {return new Transaction(accountId, amount, Instant.now());}}
AtmController handles UI flow; AtmService owns transactional integrity.
Real-world use
Real-world applications
- Hardware abstraction LLD question.
- Compensating transaction pattern practice.
- State machine for session lifecycle.
Best practices
- DIP for all hardware and bank connections.
- Rollback on partial failure.
- Never store PIN — validate remotely or hash.
- Idempotent transaction ids for retry.
- Separate read-only balance from withdraw path.
Common mistakes
- Monolithic ATM class with pin + cash + sql.
- No rollback when dispense fails.
- Storing plaintext PIN on card model.
- Skipping transaction audit log.
Advanced interview questions
Q1BeginnerMain ATM classes?
Q2BeginnerDispense fails after debit?
Q3IntermediateHow model deposit?
Q4IntermediateConcurrent access same account?
Q5AdvancedDesign ATM chain with shared cash replenishment.
Summary
ATM splits UI, service, hardware, bank host. Compensating credit on dispense failure. Session state machine for authenticated flows. Transaction log for audit trail. DIP enables testing without real hardware.