Bird
Raised Fist0

Wallet System OOP - Account, Transaction, Transfer, Ledger, Statement

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Design a Wallet System OOP
40 min ยท medium
medium

Design a wallet system that supports multiple accounts, records transactions and transfers, maintains a ledger for audit, and generates statements. The system must handle concurrent updates safely and allow extensibility for new transaction types.

  • Each Account has a unique ID, balance, and owner information.
  • Transactions can be deposits, withdrawals, or transfers between accounts.
  • Ledger maintains an immutable record of all transactions for audit purposes.
  • Statements summarize transactions over a period for an account.
  • Concurrent transactions on the same account must not cause inconsistent balances.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Account and its state management
Design the Account class with fields for ID, balance, and owner. Decide how to safely update balance concurrently.
๐Ÿ’ก Hint
Use volatile or synchronized to ensure visibility and atomicity of balance updates in Java.
Use private volatile double balance with public synchronized boolean updateBalance(double amount) to atomically check and update balance ensuring thread visibility and atomicity.
Mid: Uses synchronized method for balance update. Senior: Explains how volatile ensures visibility and synchronized prevents race conditions during balance update.
โš ๏ธCommon Wrong Answer
Using non-volatile double balance with unsynchronized updates leads to race conditions causing lost updates and inconsistent balances.
Step 2
Model Transaction hierarchy
Design Transaction as an abstract class or interface and define concrete transaction types like Deposit, Withdrawal, and Transfer.
๐Ÿ’ก Hint
Use abstract class for Transaction because it holds shared instance state like transactionId and timestamp; interfaces cannot hold state in Java.
abstract class Transaction(transactionId, timestamp) with concrete subclasses Deposit, Withdrawal, Transfer to hold shared state and behavior.
Mid: Uses inheritance for transaction types. Senior: Explains why abstract class is chosen over interface due to shared instance fields and partial implementation.
โš ๏ธCommon Wrong Answer
Using interface for Transaction with state fields leads to compilation errors since interfaces cannot hold instance state in Java.
Step 3
Implement Ledger as immutable transaction log
Design Ledger to record all transactions immutably and support concurrent appends.
๐Ÿ’ก Hint
Use a thread-safe collection like CopyOnWriteArrayList or synchronize append method to ensure thread-safe mutation.
Ledger uses private final List<Transaction> transactions with public synchronized void addTransaction(Transaction t) to ensure thread-safe append.
Mid: Synchronizes addTransaction method. Senior: Explains how synchronization prevents concurrent modification exceptions and ensures visibility of new transactions.
โš ๏ธCommon Wrong Answer
Using ArrayList without synchronization causes ConcurrentModificationException or lost transactions under concurrent writes.
Step 4
Design Statement generation
Design Statement class to generate summaries of transactions for an account over a date range.
๐Ÿ’ก Hint
Use aggregation relationship: Statement aggregates Transactions but does not own them; transactions exist independently in Ledger.
Statement aggregates List<Transaction> filtered by account and date range; uses immutable snapshot to avoid concurrent modification.
Mid: Filters transactions by account and date. Senior: Explains aggregation lifecycle where Statement does not own Transactions and uses defensive copying for thread safety.
โš ๏ธCommon Wrong Answer
Statement holds direct reference to mutable transaction list causing data inconsistency if transactions change concurrently.
Step 5
Handle Transfer transaction concurrency
Design Transfer transaction to atomically debit one account and credit another without deadlocks.
๐Ÿ’ก Hint
Use synchronized blocks locking accounts in consistent order (e.g., by account ID) to avoid deadlocks and ensure atomicity.
Transfer uses synchronized blocks locking both involved Account objects in ascending order of accountId to prevent deadlocks and ensure atomic balance updates.
Mid: Locks both accounts during transfer. Senior: Explains deadlock scenario if locks acquired in inconsistent order and how ordering prevents it.
โš ๏ธCommon Wrong Answer
Synchronizing accounts in arbitrary order causes deadlocks when two threads try to transfer funds between same accounts in opposite directions.
๐Ÿ”Œ
API Design Challenge
Design a RESTful API for the Wallet System that supports account creation, transaction processing, transfer operations, ledger querying, and statement generation.
  • Use resource-oriented URLs like /accounts, /transactions, /transfers, /ledger, /statements.
  • Support pagination and filtering for ledger and statement endpoints to handle large data sets.
  • Ensure idempotency for transaction and transfer endpoints to prevent duplicate processing.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • Account.balance declared volatile and updated via synchronized method
  • Transaction as abstract class with shared instance state
  • Ledger uses synchronized method to append transactions
  • Transfer synchronizes on Account objects in consistent order to avoid deadlocks
  • Statement aggregates transactions with defensive copying to avoid concurrent modification
Good to Have - 30%
  • Use enum TransactionType for type safety
  • Immutable transactionId and timestamp fields
  • Use unmodifiable collections for external access
  • Clear lifecycle ownership: composition for Account balance, aggregation for Statement transactions
  • Explain Java memory model guarantees for volatile and synchronized
Bonus - 10%
  • Explain double-checked locking if singleton Ledger is used
  • Use AtomicLong for transactionId generation
  • Implement optimistic locking for balance updates
  • Use CopyOnWriteArrayList for Ledger transactions
  • Explain happens-before relationship in concurrency
Using String for transaction types leads to runtime typo bugs and no exhaustive switch checking
Non-volatile balance with unsynchronized updates causes lost updates and inconsistent state
Using interface for Transaction with instance fields causes compilation errors
Unsynchronized ArrayList in Ledger causes ConcurrentModificationException under concurrency
Arbitrary lock ordering in Transfer causes deadlocks when two threads try to transfer funds between same accounts in opposite directions
โœ…
Model Answer
โ–ผ Reveal

This design models a Wallet System with thread-safe Account balance updates, an abstract Transaction hierarchy, an immutable Ledger for audit, Statement generation with defensive copying, and deadlock-free Transfer concurrency.

  • Use private volatile double balance in Account with synchronized updateBalance to ensure visibility and atomicity.
  • Implement Transaction as abstract class to hold shared immutable state; concrete subclasses for each transaction type.
  • Ledger uses synchronized method to append transactions to a private final List to ensure thread safety.
  • Statement aggregates transactions without ownership; uses defensive copying to provide immutable snapshots.
  • Transfer synchronizes on Account locks in consistent order by accountId to avoid deadlocks during concurrent transfers.
  • Use enum TransactionType for compile-time safety and exhaustive switch statements.
  • TransactionId and timestamp fields are immutable to guarantee thread safety.
  • Expose unmodifiable views or copies of internal collections to clients to prevent external mutation.
  • Composition is used for Account owning balance and owner info; aggregation for Statement referencing transactions.
  • Synchronized methods and volatile fields enforce Java memory model happens-before relationships for concurrency guarantees.
  • Account class uses private volatile double balance and public synchronized boolean updateBalance(double amount) to ensure atomic and visible balance updates.
  • Transaction is an abstract class holding shared state like transactionId and timestamp; concrete subclasses implement specific transaction types.
  • Ledger maintains a private final List<Transaction> with synchronized addTransaction method to ensure thread-safe appends.
  • Statement aggregates transactions filtered by account and date range; uses defensive copying to avoid concurrent modification.
  • Transfer transaction synchronizes on involved Account objects in consistent order by accountId to prevent deadlocks.
  • Use enum TransactionType for compile-time safety and exhaustive switch statements in transaction processing.
  • Use immutable fields for transactionId and timestamp to ensure thread safety and immutability of Transaction objects.
  • Avoid exposing internal mutable collections directly; provide unmodifiable views or copies to clients.
  • Use composition for Account owning balance and owner info; aggregation for Statement referencing transactions without ownership.
  • Use synchronized methods and volatile fields to enforce happens-before relationships and visibility guarantees in Java concurrency.

Practice

(1/5)
1. In the Elevator System design, the ElevatorController class uses a private constructor and a static synchronized getInstance() method to provide a single global access point. Which design pattern is applied here and why is it the correct choice?
easy
A. Factory pattern, because it creates ElevatorController instances on demand.
B. Singleton pattern, to ensure only one ElevatorController manages all elevators centrally.
C. Builder pattern, to incrementally construct ElevatorController with complex configuration.
D. Prototype pattern, to clone ElevatorController for each elevator instance.

Solution

  1. Step 1: Identify Singleton usage

    The private constructor and static synchronized getInstance() method restrict instantiation to one object, ensuring a single ElevatorController.
  2. Step 2: Why Singleton fits ElevatorController

    ElevatorController coordinates multiple elevators and floor queues centrally, so multiple instances would cause inconsistent state and scheduling conflicts.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Singleton ensures one global ElevatorController instance [check]
Hint: Singleton restricts to one instance for centralized control [check]
Common Mistakes:
2. In the auction system, the placeBid() method checks if the auction is still open before accepting a bid. Without synchronizing placeBid(), which exact thread interleaving causes two threads to accept bids after the auction closes, resulting in inconsistent state?
medium
A. Thread 1 reads auction open -> false; Thread 2 reads auction open -> true; both reject bids.
B. Thread 1 and Thread 2 both wait on a lock; no bids accepted until lock released.
C. Thread 1 writes bid; Thread 2 reads bid; no race condition occurs because writes are atomic.
D. Thread 1 reads auction open -> true; context switch before bid recorded; Thread 2 reads auction open -> true; both record bids after auction closed.

Solution

  1. Step 1: Identify check-then-act race

    Without synchronization, Thread 1 reads auction open as true but is preempted before recording the bid.
  2. Step 2: Thread 2 reads auction open as true and proceeds to record bid as well, both bypassing the closed state check.

  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Unsynchronized check-then-act -> double acceptance after close [check]
Hint: Check-then-act without sync -> race condition double accept [check]
Common Mistakes:
3. The current Tic-Tac-Toe design uses a WinStrategy interface for win detection. To add a WeekendWinBonusStrategy that applies only on weekends, what is the minimum change required to comply with SOLID principles?
medium
A. Create a new WeekendWinBonusStrategy class implementing WinStrategy and use it without modifying existing classes.
B. Add a boolean flag in the Board class to toggle weekend mode and update all win checks accordingly.
C. Modify the existing WinStrategy implementations to check the day and apply bonus logic.
D. Combine all win detection logic including weekend bonus into a single monolithic class.

Solution

  1. Step 1: Understand OCP and DIP in current design

    Open-Closed Principle (OCP) requires adding new behavior without modifying existing classes. Dependency Inversion Principle (DIP) encourages depending on abstractions.
  2. Step 2: Evaluate options for minimal change

    Create a new WeekendWinBonusStrategy class implementing WinStrategy and use it without modifying existing classes. adds a new strategy class but requires changing client code to use it, violating minimal change. Add a boolean flag in the Board class to toggle weekend mode and update all win checks accordingly. adds a toggle flag in Board, allowing existing strategies to adapt behavior without modifying their code, preserving OCP better than A or D.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Toggle flag in Board -> minimal change, respects OCP [check]
Hint: OCP: add new class or toggle flag, avoid modifying existing [check]
Common Mistakes:
4. You need to add support for ElectricCar that requires ChargingSpot compatibility in the current parking system. What is the minimum change to the design to support this without modifying existing classes?
hard
A. Modify Vehicle class to add a boolean needsCharging and update parking logic to check this flag.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly.
C. Create a new ElectricParkingLot subclass that handles electric cars and charging spots separately.
D. Add a ChargingSpot field to Vehicle and modify all parking methods to handle it.

Solution

  1. Step 1: Identify extensibility requirements

    ElectricCar and ChargingSpot are new types requiring compatibility mapping without changing existing classes.
  2. Step 2: Apply minimal invasive change

    Adding new enum values and subclass plus updating compatibility map extends functionality without modifying existing class code, preserving OCP.
  3. Step 3: Avoid violating OCP

    Options B, C, and D modify existing classes or create parallel hierarchies, increasing complexity and violating OCP.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Extend enums and map, subclass ElectricCar -> minimal change [check]
Hint: Extend enums and map, subclass new type -> minimal change [check]
Common Mistakes:
5. You need to add support for ElectricCar and ChargingSpot in the parking system without modifying existing classes. What is the minimum change to the current design?
hard
A. Modify Vehicle and Spot classes to include electric-specific fields and logic.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update compatibility map externally.
C. Create a new ElectricVehicle interface and make ElectricCar implement it, modifying existing classes to check for this interface.
D. Add a boolean isElectric flag to Vehicle and Spot classes and handle logic with conditionals.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Enums can be extended with new constants, and new subclasses can be added without changing existing code.
  2. Step 2: Use external compatibility map update

    Updating the compatibility map externally avoids modifying existing classes, preserving OCP and avoiding class explosion.
  3. Step 3: Avoid flags or interface checks that require existing class changes

    Flags or interface checks require modifying existing classes, violating OCP.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Extend enums + new subclass + external map update -> minimal change [check]
Hint: Extend enums + subclass + external map -> minimal change [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard