2Brute2Best
🔥0
P9

Design an ATM & Cash Dispenser

Medium High-frequency interview topic Pro OnlyStateChain of Responsibility std::expected (C++23)
⚡ 30-Second Interview Cheat SheetP9 · 45-Min Design Plan
3 Patterns Combined
1. Scope Boundaries

In a 45-minute LLD interview, the interviewer evaluates two core architectural capabilities: lifecycle security via the State pattern (enforcing operations only when authenticated, ejecting on 3 failed PIN attempts) and atomic currency dispensing via Chain of Responsibility (two-phase plan/commit ensuring zero cassette inventory mutation on shortfall). Scope the solution to session lifecycle states, multi-denomination cascading (₹2000 → ₹500 → ₹100), explicit error handling via std::expected, and clean card session ejection. Mention cash deposits, Core Banking System (CBS) two-phase commit, and network timeout compensation as extensions.

2. Core Noun Entities
Carddata-struct
AtmStateinterface
AtmMachinestateful-class
NoteDispenserstateful-class
3. Hotspot → Pattern
Session security lifecycle (Card insertion, PIN verification, Cash withdrawal) → State (AtmState with IdleState, CardInsertedState, PinVerifiedState)
Multi-denomination note dispensing with exact-change rollback → Chain of Responsibility with two-phase plan/commit (NoteDispenser)
Domain failure reporting without exceptions → std::expected<map<int, int>, string> (C++23 explicit failure modeling)
01

Problem statement & clarifying requirements

Design an Automated Teller Machine (ATM) and Cash Dispenser. The system must manage a secure customer session lifecycle (Idle → CardInserted → PinVerified) while rejecting invalid actions at each stage. Cash withdrawal must be coordinated across currency denominations (₹2000, ₹500, ₹100) using a Chain of Responsibility that guarantees atomic rollback—if the ATM cannot satisfy the exact amount with its available notes, no notes may be deducted from the machine cassettes.

Questions you should ask the interviewer first
“What happens if a user inputs an invalid PIN 3 consecutive times?”

The card is retained/ejected for security, and the ATM transitions immediately back to IdleState.

“How is cash dispensed across multiple denominations?”

Higher denominations (₹2000) are evaluated first to minimize note count, cascading down to ₹500 and ₹100.

“What happens if an ATM has enough total money (e.g. ₹2000) but cannot make exact change for ₹800?”

The withdrawal transaction fails atomically. A two-phase plan-and-commit protocol ensures zero notes are deducted if the request cannot be completely satisfied.

“How are withdrawal errors reported back to the user?”

Using C++23 std::expected<std::map<int, int>, std::string>, making domain failures explicit without relying on exception handling.

02

First, sketch the design yourself

Spend 5 minutes listing the classes, who owns whom, and which interface hides the part that changes.

  1. 1.

    Model session transitions as discrete States: Avoid giant switch(currentStatus) blocks inside every method. Each state (Idle, CardInserted, PinVerified) implements an AtmState interface and handles only valid operations.

  2. 2.

    Guarantee atomic currency dispensation: Never greedily deduct physical notes from cassettes during traversal. Run a planning phase that computes the denomination breakdown; only if the remainder reaches zero do you execute the commit phase.

  3. 3.

    Separate hardware cassette inventory from banking cards: The ATM machine manages the NoteDispenser chain, while the Card entity encapsulates customer credentials and account balance.

03

How to think through this problem (5-step walkthrough)

Step 1 · Scope the 45-minute target & core boundary

In a 45-minute LLD interview, the interviewer evaluates two core architectural capabilities: lifecycle security via the State pattern (enforcing operations only when authenticated, ejecting on 3 failed PIN attempts) and atomic currency dispensing via Chain of Responsibility (two-phase plan/commit ensuring zero cassette inventory mutation on shortfall). Scope the solution to session lifecycle states, multi-denomination cascading (₹2000 → ₹500 → ₹100), explicit error handling via std::expected, and clean card session ejection. Mention cash deposits, Core Banking System (CBS) two-phase commit, and network timeout compensation as extensions.

Step 2 · Extract candidate nouns (and reject false classes)

Not every noun in the prompt deserves its own class. Separate dumb data structs from stateful managers, and turn trivial nouns into fields or enums.

Card
Value Struct

Value struct representing customer banking card credentials (card number, PIN hash, account balance).

AtmState
Abstract Interface

State pattern interface defining permitted operations for each lifecycle stage (insertCard, enterPin, withdrawCash, ejectCard).

AtmMachine
Stateful Class / Manager

Context entity holding the active state (std::unique_ptr<AtmState>), current card session, failed PIN attempt counter, and the note dispenser chain.

NoteDispenser
Stateful Class / Manager

Chain of Responsibility handler managing a specific denomination cassette (e.g. ₹2000, ₹500, ₹100) and chaining to next_.

PinNumber / Keypad
Reject as Class → Field / Method

Trap noun — beginners often introduce separate PinNumber, Keypad, or CardSlot classes. A PIN is a simple std::string_view parameter passed into enterPin(), and the keypad/card-slot are hardware I/O interfaces that simply trigger ATM API calls.

Step 3 · Draft the naive design & spot what breaks

A naive implementation relies on boolean flags (isCardInserted, isPinVerified) and massive switch (status) blocks across every method. Deducting notes greedily in a single loop (notes_[2000] -= count; notes_[500] -= count;) causes severe corruption: if the ATM has one ₹2000 note and requests ₹2100, the machine deducts the ₹2000 note, fails to find a ₹100 note, and either crashes or leaves the cassette ₹2000 lighter without giving the customer their money.

Step 4 · Map change hotspots to design patterns
Session security lifecycle (Card insertion, PIN verification, Cash withdrawal)→ State (AtmState with IdleState, CardInsertedState, PinVerifiedState)

Scattering boolean authentication flags across all methods creates invalid states (e.g. withdrawing money without entering a PIN or inserting a card when a session is active).

Multi-denomination note dispensing with exact-change rollback→ Chain of Responsibility with two-phase plan/commit (NoteDispenser)

A single monolithic dispensing loop cannot easily accommodate new cassettes or support atomic two-phase rollback when exact change is impossible.

Domain failure reporting without exceptions→ std::expected<map<int, int>, string> (C++23 explicit failure modeling)

Using raw boolean flags loses the reason for failure; throwing C++ exceptions for expected business rejections incurs stack-unwinding performance penalties.

Step 5 · Lock down C++23 ownership, invariants & concurrency
  • ✓

    Two-phase atomic dispense invariant: NoteDispenser::planDispense() computes the required note counts into a trial plan map without mutating cassette inventory; commitDispense() is invoked only if the remaining amount reaches exactly zero.

  • ✓

    Security state invariant: Cash withdrawal is permitted exclusively in PinVerifiedState. Three consecutive failed PIN attempts in CardInsertedState immediately eject the card and revert the machine to IdleState.

  • ✓

    Cassette ownership invariant: AtmMachine exclusively owns the head of the dispenser chain via std::unique_ptr<NoteDispenser>, and each dispenser node exclusively owns its downstream successor next_.

04

Entities & design patterns used

Brute2Best Pro Content

Unlock Full UML Diagram & C++23 Solution

The complete UML diagram, production-ready C++23 code, follow-up extensions, and design pitfall analysis are part of Brute2Best Pro. All 7 foundation chapters are 100% free.

All 440 visual dry runs & algorithms
Interactive step-by-step playback with pointer & array state transitions
52 comprehensive LLD chapters
Interactive UML diagrams, real-world stories (Food Delivery, Trading, RPG), and practice solutions
40 C++ basics labs & hardware memory models
Vtables, byte layouts, assembly insights, and multi-threading race conditions
Production-ready C++23 source code
Modern idioms, clean contracts, time/space complexity derivations, and interview takeaways

First anchor question per pattern, Part 1 LLD & Module 1 C++ basics are 100% Free forever.

Systems Integration

Related C++ Systems & Algorithmic Foundations

Deep dive into underlying layers