📂
Interview Prep
LLD Questions
LLD Questions
  • LLD Introduction
  • Meeting Scheduler
  • Distributed Cache System
  • Rate limiter
  • Multi-Tier Elevator System
  • MyGate System
  • Uber Sytem LLD
  • Parking Lot System
  • Online book store
  • Library Management System
  • Movie Ticket Booking System
  • Hotel Management System
  • File Storage System
    • Solution 2
  • Chat Application
  • Social Media Platform
  • Notification System
  • Airline Reservation System
  • ATM System
  • E-commerce Website
  • Food Delivery System
  • Shopping Cart System
  • URL Shortener system
  • Chess Tournament System
  • Threading therory
  • OOP questions
Powered by GitBook
On this page
  • Explanation of Key Points & Design Patterns
  • Conclusion

ATM System

Below is a comprehensive Low-Level Design (LLD) for an ATM System implemented in Java. This design covers the following aspects:

  • Key Classes and Their Interactions:

    • ATM: Represents the ATM machine that handles user interactions and transaction processing.

    • User & Account: Represent a bank customer and his/her associated bank account. The user holds the card information (card number, PIN) and is linked to an Account which stores balance and account data.

    • Transaction: An abstract representation of a banking transaction. Concrete transactions include Withdrawal, Deposit, and BalanceEnquiry.

    • AuthenticationService: Handles user authentication securely.

    • TransactionFactory: Uses the Factory Pattern to create specific transaction objects based on a type.

  • User Authentication: The AuthenticationService (a Singleton) securely authenticates users using their card number and PIN. In a real-world system, this service would interface with a secure backend or an HSM (hardware security module) for encryption and validation.

  • Transaction Handling:

    • Cash Dispensing: Implemented in the Withdrawal transaction, which checks the account balance, deducts the requested amount, and (in a real ATM) would trigger the cash dispenser hardware.

    • Balance Enquiry: The BalanceEnquiry transaction simply reads the account balance.

    • Account Updates: The Deposit transaction credits the account. All transactions implement an execute() method that performs the necessary operations. The TransactionFactory (using the Factory Pattern) is responsible for creating the correct Transaction instance based on the user’s request. We also include a TransactionManager within the ATM (using the Singleton Pattern) to process the transactions in a uniform way.

  • Design Patterns in the System:

    • Singleton Pattern: Used in classes like ATM and AuthenticationService to ensure only one instance exists in the system.

    • Factory Pattern: Implemented via the TransactionFactory to centralize transaction object creation.

    • Strategy Pattern (Extension Point): We use polymorphism with the Transaction interface to allow different transaction strategies (Withdrawal, Deposit, BalanceEnquiry) to be executed in a unified manner.

    • DAO/Repository (Conceptual): In a production system, data access for Users and Accounts would be abstracted via repositories; here, we use in-memory data for simplicity.

Below is the detailed Java code with inline comments explaining the design and patterns:


import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

// --------------------- ENTITIES ---------------------

// Account class representing a bank account.
class Account {
    private String accountNumber;
    private double balance;
    
    public Account(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }
    
    public synchronized double getBalance() {
        return balance;
    }
    
    // Deposit money into the account.
    public synchronized void deposit(double amount) {
        balance += amount;
    }
    
    // Withdraw money from the account if sufficient balance exists.
    public synchronized boolean withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
    
    public String getAccountNumber() {
        return accountNumber;
    }
}

// User class representing a bank customer with authentication details.
class User {
    private String cardNumber;
    private String pin;
    private Account account;
    
    public User(String cardNumber, String pin, Account account) {
        this.cardNumber = cardNumber;
        this.pin = pin;
        this.account = account;
    }
    
    public String getCardNumber() {
        return cardNumber;
    }
    
    public String getPin() {
        return pin;
    }
    
    public Account getAccount() {
        return account;
    }
}

// --------------------- AUTHENTICATION SERVICE ---------------------

/*
 * Singleton Pattern: AuthenticationService is responsible for securely authenticating users.
 */
class AuthenticationService {
    private static AuthenticationService instance;
    // In-memory storage for simplicity (in real systems, this is a secured DB or service).
    private Map<String, User> userDatabase;
    
    private AuthenticationService() {
        userDatabase = new ConcurrentHashMap<>();
        // Pre-load with dummy data.
        // Card number, PIN, and an account with initial balance.
        userDatabase.put("1234567890", new User("1234567890", "1234", new Account("ACCT001", 1000.0)));
        userDatabase.put("0987654321", new User("0987654321", "4321", new Account("ACCT002", 2000.0)));
    }
    
    public static synchronized AuthenticationService getInstance() {
        if (instance == null) {
            instance = new AuthenticationService();
        }
        return instance;
    }
    
    // Authenticate user by card number and PIN.
    public User authenticate(String cardNumber, String pin) {
        User user = userDatabase.get(cardNumber);
        if (user != null && user.getPin().equals(pin)) {
            return user;
        }
        return null;
    }
}

// --------------------- TRANSACTION INTERFACE & IMPLEMENTATIONS ---------------------

/*
 * Strategy Pattern (via polymorphism): Transaction is an interface that defines the execute() method.
 * Different transaction types implement this interface.
 */
interface Transaction {
    void execute();
}

// Concrete transaction for cash withdrawal.
class Withdrawal implements Transaction {
    private Account account;
    private double amount;
    
    public Withdrawal(Account account, double amount) {
        this.account = account;
        this.amount = amount;
    }
    
    @Override
    public void execute() {
        // Securely process cash withdrawal.
        if (account.withdraw(amount)) {
            System.out.println("Withdrawal of $" + amount + " successful. New balance: $" + account.getBalance());
            // In a real ATM, hardware integration for cash dispensing would occur here.
        } else {
            System.out.println("Insufficient funds for withdrawal of $" + amount + ". Current balance: $" + account.getBalance());
        }
    }
}

// Concrete transaction for deposit.
class Deposit implements Transaction {
    private Account account;
    private double amount;
    
    public Deposit(Account account, double amount) {
        this.account = account;
        this.amount = amount;
    }
    
    @Override
    public void execute() {
        account.deposit(amount);
        System.out.println("Deposit of $" + amount + " successful. New balance: $" + account.getBalance());
    }
}

// Concrete transaction for balance enquiry.
class BalanceEnquiry implements Transaction {
    private Account account;
    
    public BalanceEnquiry(Account account) {
        this.account = account;
    }
    
    @Override
    public void execute() {
        System.out.println("Current balance: $" + account.getBalance());
    }
}

// --------------------- TRANSACTION FACTORY ---------------------

/*
 * Factory Pattern: TransactionFactory centralizes creation of Transaction objects.
 */
class TransactionFactory {
    public static Transaction createTransaction(String type, Account account, double amount) {
        switch (type.toUpperCase()) {
            case "WITHDRAWAL":
                return new Withdrawal(account, amount);
            case "DEPOSIT":
                return new Deposit(account, amount);
            case "BALANCE":
                return new BalanceEnquiry(account);
            default:
                throw new IllegalArgumentException("Invalid transaction type");
        }
    }
    
    // Overloaded method for transactions that do not require an amount (like balance enquiry).
    public static Transaction createTransaction(String type, Account account) {
        return createTransaction(type, account, 0);
    }
}

// --------------------- ATM CLASS ---------------------

/*
 * Singleton Pattern: ATM represents the physical machine and uses a single instance to process transactions.
 * It interacts with AuthenticationService and uses TransactionFactory to handle various transactions.
 */
class ATM {
    private static ATM instance;
    private AuthenticationService authService;
    
    private ATM() {
        authService = AuthenticationService.getInstance();
    }
    
    public static synchronized ATM getInstance() {
        if (instance == null) {
            instance = new ATM();
        }
        return instance;
    }
    
    // Simulate user interaction at the ATM.
    public void start() {
        Scanner scanner = new Scanner(System.in);
        
        // User authentication
        System.out.println("Welcome to the ATM. Please insert your card (enter card number):");
        String cardNumber = scanner.nextLine();
        System.out.println("Enter PIN:");
        String pin = scanner.nextLine();
        
        User user = authService.authenticate(cardNumber, pin);
        if (user == null) {
            System.out.println("Authentication failed. Please try again.");
            scanner.close();
            return;
        }
        System.out.println("Authentication successful. Welcome, " + user.getAccount().getAccountNumber());
        
        // Transaction processing loop.
        boolean sessionActive = true;
        while (sessionActive) {
            System.out.println("\nSelect Transaction: 1. Withdrawal  2. Deposit  3. Balance Enquiry  4. Exit");
            String choice = scanner.nextLine();
            
            Transaction transaction = null;
            switch (choice) {
                case "1":
                    System.out.println("Enter withdrawal amount:");
                    double withdrawalAmount = Double.parseDouble(scanner.nextLine());
                    transaction = TransactionFactory.createTransaction("WITHDRAWAL", user.getAccount(), withdrawalAmount);
                    break;
                case "2":
                    System.out.println("Enter deposit amount:");
                    double depositAmount = Double.parseDouble(scanner.nextLine());
                    transaction = TransactionFactory.createTransaction("DEPOSIT", user.getAccount(), depositAmount);
                    break;
                case "3":
                    transaction = TransactionFactory.createTransaction("BALANCE", user.getAccount());
                    break;
                case "4":
                    sessionActive = false;
                    System.out.println("Thank you for using the ATM.");
                    continue;
                default:
                    System.out.println("Invalid choice. Try again.");
                    continue;
            }
            
            // Execute the chosen transaction.
            if (transaction != null) {
                transaction.execute();
            }
        }
        scanner.close();
    }
}

// --------------------- MAIN APPLICATION ---------------------

public class ATMSystemApp {
    public static void main(String[] args) {
        // Get the single instance of the ATM and start the session.
        ATM atm = ATM.getInstance();
        atm.start();
    }
}

Explanation of Key Points & Design Patterns

Key Classes and Their Interactions

  • ATM: The central class that interacts with the user. It obtains user credentials, authenticates via AuthenticationService, and processes transactions using TransactionFactory.

  • User & Account: The User class holds authentication details (card number, PIN) and links to an Account that stores balance and account number.

  • Transaction & Its Subclasses: Each transaction type (Withdrawal, Deposit, BalanceEnquiry) implements the execute() method. This allows the ATM to process any transaction in a unified way (Strategy Pattern via polymorphism).

  • AuthenticationService: A Singleton service that securely authenticates users using in-memory data (in a real system, this would connect to a secure backend).

  • TransactionFactory: Implements the Factory Pattern to create the appropriate Transaction object based on the user’s request.

User Authentication and Secure Processing

  • AuthenticationService (Singleton): Validates user credentials securely. If authentication fails, no transaction is processed.

Transaction Handling

  • Withdrawal: Checks if the account has sufficient funds before deducting the requested amount and (in a real ATM) triggers cash dispensing.

  • Deposit: Credits the account with the deposited amount.

  • Balance Enquiry: Reads the current balance from the account.

  • TransactionFactory (Factory Pattern): Simplifies the creation of Transaction objects by centralizing the logic in one place.

Design Patterns Used

  • Singleton Pattern: Ensures only one instance of critical classes (ATM, AuthenticationService) exists.

  • Factory Pattern: Used by TransactionFactory to create transaction objects.

  • Strategy Pattern (via polymorphism): Different transactions (Withdrawal, Deposit, BalanceEnquiry) share a common interface, enabling interchangeable behavior when executing transactions.

  • DAO/Repository (Conceptual): While not fully implemented, user and account data management would typically be abstracted behind repository interfaces in a production system.


Conclusion

This detailed LLD for an ATM System demonstrates:

  • How key classes (ATM, User, Account, Transaction) interact to process transactions.

  • How user authentication is handled securely through a Singleton AuthenticationService.

  • How transactions such as cash dispensing, balance enquiries, and deposits are executed using a common Transaction interface and created via a TransactionFactory.

  • How the Strategy Pattern is integrated by letting each Transaction subclass implement its own execution logic.

This modular and extensible design employs multiple LLD design patterns to ensure the system is maintainable, scalable, and secure—a robust blueprint suitable for technical interviews and real-world implementations.

PreviousAirline Reservation SystemNextE-commerce Website

Last updated 2 months ago