Skip to main content

Design a ATM system

Problem Statement

Design an ATM system that allows users to withdraw cash, check balance, and deposit money using a debit card. The system should validate the card, authenticate the user using a PIN, and interact with the bank account securely. It should also handle cash dispensing and ensure account balances and ATM cash are updated correctly after every transaction.


Functional Requirements

  • Support card-based authentication
  • Require PIN verification before transactions
  • Allow users to check account balance
  • Allow cash withdrawal
  • Allow cash deposit
  • Dispense cash only if ATM and account both have sufficient balance
  • Maintain ATM cash inventory
  • Record all transactions for audit purposes

Objects Required

  • ATM
  • Card
  • Customer (logical user)
  • BankAccount
  • Transaction
  • TransactionType
  • CashDispenser

TransactionType Enum


public enum TransactionType {
    WITHDRAW,
    DEPOSIT,
    BALANCE_CHECK
}

The TransactionType enum defines the supported ATM operations. It avoids using raw strings and keeps transaction classification consistent across the system.


Card Class


public class Card {

    private String cardNumber;
    private int pin;
    private BankAccount account;

    public Card(String cardNumber, int pin, BankAccount account) {
        this.cardNumber = cardNumber;
        this.pin = pin;
        this.account = account;
    }

    public boolean validatePin(int enteredPin) {
        return this.pin == enteredPin;
    }

    public BankAccount getAccount() {
        return account;
    }
}

The constructor initializes the card and links it to a specific bank account.

The validatePin() method verifies whether the entered PIN matches the stored PIN. This acts as the first security checkpoint before any ATM operation is allowed.

The getAccount() method returns the linked bank account so that ATM operations can be performed without exposing internal account details directly.


BankAccount Class


public class BankAccount {

    private String accountNumber;
    private double balance;

    public BankAccount(String accountNumber, double balance) {
        this.accountNumber = accountNumber;
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }

    public void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
        }
    }

    public void deposit(double amount) {
        balance += amount;
    }
}

The constructor sets up the account with an account number and initial balance.

The getBalance() method returns the current available balance for display or validation.

The withdraw() method reduces the balance only if sufficient funds exist, preventing overdraft situations.

The deposit() method increases the account balance when money is added through the ATM.


Transaction Class


import java.time.LocalDateTime;

public class Transaction {

    private String transactionId;
    private TransactionType type;
    private double amount;
    private LocalDateTime timestamp;
    private BankAccount account;

    public Transaction(String transactionId,
                       TransactionType type,
                       double amount,
                       BankAccount account) {

        this.transactionId = transactionId;
        this.type = type;
        this.amount = amount;
        this.account = account;
        this.timestamp = LocalDateTime.now();
    }
}

The constructor creates a transaction record whenever an ATM operation occurs.

The Transaction class stores the type, amount, timestamp, and linked account to maintain a complete audit trail of ATM activity.


CashDispenser Class


public class CashDispenser {

    private double cashAvailable;

    public CashDispenser(double cashAvailable) {
        this.cashAvailable = cashAvailable;
    }

    public boolean canDispense(double amount) {
        return cashAvailable >= amount;
    }

    public void dispense(double amount) {
        if (canDispense(amount)) {
            cashAvailable -= amount;
        }
    }

    public void refill(double amount) {
        cashAvailable += amount;
    }
}

The constructor initializes the ATM with available cash.

The canDispense() method checks whether the ATM has enough cash before approving a withdrawal.

The dispense() method reduces ATM cash after a successful withdrawal.

The refill() method is used by bank staff to reload cash into the ATM machine.


ATM Class


import java.util.UUID;

public class ATM {

    private CashDispenser dispenser;

    public ATM(CashDispenser dispenser) {
        this.dispenser = dispenser;
    }

    public void checkBalance(Card card) {

        System.out.println("Balance: " + card.getAccount().getBalance());
    }

    public void withdraw(Card card, int pin, double amount) {

        if (!card.validatePin(pin)) {
            System.out.println("Invalid PIN");
            return;
        }

        if (!dispenser.canDispense(amount)) {
            System.out.println("ATM has insufficient cash");
            return;
        }

        BankAccount account = card.getAccount();

        if (account.getBalance() < amount) {
            System.out.println("Insufficient account balance");
            return;
        }

        account.withdraw(amount);
        dispenser.dispense(amount);

        System.out.println("Please collect your cash");
    }

    public void deposit(Card card, int pin, double amount) {

        if (!card.validatePin(pin)) {
            System.out.println("Invalid PIN");
            return;
        }

        BankAccount account = card.getAccount();
        account.deposit(amount);

        System.out.println("Amount deposited successfully");
    }
}

The ATM class acts as the central controller for all operations.

The checkBalance() method retrieves the account balance linked to the card and displays it to the user.

The withdraw() method first validates the PIN, then checks ATM cash availability, and finally ensures sufficient account balance before completing the transaction. Only after all validations does it update both the account and ATM cash state.

The deposit() method validates the PIN and then directly updates the bank account balance.


Main Class


public class Main {

    public static void main(String[] args) {

        BankAccount account =
                new BankAccount("ACC123", 5000);

        Card card =
                new Card("CARD123", 1234, account);

        CashDispenser dispenser =
                new CashDispenser(10000);

        ATM atm =
                new ATM(dispenser);

        atm.checkBalance(card);

        atm.withdraw(card, 1234, 1000);

        atm.deposit(card, 1234, 2000);

        atm.checkBalance(card);
    }
}

The main method simulates a complete ATM session starting from balance check, followed by withdrawal and deposit operations.

It demonstrates how authentication, account updates, and cash dispensing work together in a real ATM flow.


Class Diagram

ATMdispenser : CashDispensertransactions : List<Transaction>checkBalance(card : Card) : voidwithdraw(card : Card, pin : int, amount : double) : voiddeposit(card : Card, pin : int, amount : double) : voidaddTransaction(transaction : Transaction) : voidCardcardNumber : Stringpin : intaccount : BankAccountvalidatePin(pin : int) : booleangetAccount() : BankAccountBankAccountaccountNumber : Stringbalance : doublegetBalance() : doublewithdraw(amount : double) : voiddeposit(amount : double) : voidCashDispensercashAvailable : doublecanDispense(amount : double) : booleandispense(amount : double) : voidrefill(amount : double) : voidTransactiontransactionId : Stringtype : TransactionTypeamount : doubletimestamp : LocalDateTimeaccount : BankAccountTransaction(id, type, amount, account)TransactionTypeWITHDRAWDEPOSITBALANCE_CHECK

Also See

Comments

Popular posts from this blog

Designing a Parking Lot - Low Level Design

Problem Statement Design a parking lot that can handle vehicles entering and leaving while managing parking across multiple floors. Each vehicle should be assigned a suitable parking spot based on its type, and the spot should be freed once the vehicle exits. The design should also support generating a ticket at entry and optionally calculating the parking fee based on the duration of stay. Asked In Companies Amazon Google Microsoft Uber Walmart Flipkart Meta PayPal Oracle Salesforce Adobe Apple Intuit LinkedIn Atlassian Functional Requirements The design should support multiple vehicle types such as bikes, cars, and trucks A vehicle must be assigned a parking spot compatible with its type A parking spot cannot be assigned to more than one vehicle at a time The parking lot should support multiple levels (floors) The design should search and allocate an availa...

Most Frequently Asked Low Level Design(LLD) Interview Questions

Below are the curated list of most commonly asked Low Level Design (LLD) interview problems. Each problem includes a short description and a link to the complete solution with code and class diagrams. Design Parking Lot System The system should handle parking for different vehicle types such as bikes, cars, and trucks. It should manage slot allocation, availability tracking, and entry/exit flow. The design also ensures efficient usage of parking space under varying load conditions. View Solution Design Elevator / Lift System The system should support multiple elevators operating across floors with request handling logic. It focuses on scheduling algorithms to minimize wait time and optimize movement. It also manages direction control and concurrent floor requests. View Solution Design Movie Ticket Booking System The system should allow users to browse movies, select shows, and book seats. It handles seat ...

Software Design Patterns for LLD Interviews: A Complete Guide

Software Design Patterns for LLD Interviews: A Complete Guide In Software Development Engineer (SDE) interviews—especially for mid-level and senior roles—low-level design (LLD) rounds assess your ability to write clean, reusable, maintainable, and extensible code. The foundation of resolving these architectural challenges lies in the standard Gang of Four (GoF) Design Patterns. Rather than memorizing theoretical definitions, interviewers expect you to apply these patterns to real-world scenarios, identifying the trade-offs of each. Below is a comprehensive guide to the 12 most frequently asked design patterns in LLD interviews, categorized by their classification (Creational, Structural, and Behavioral). Each pattern contains a concrete, real-world Java implementation and a detailed breakdown of design decisions. Creational Design Patterns Creational design patterns deal with object creation mechanisms. They abstract the instantiation process, making a system independent of how...