Bird
Raised Fist0

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

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
🏗️
Wallet System OOP - Account, Transaction, Transfer, Ledger, Statement
Design a wallet system with accounts, transactions, transfers, ledger, and statements using Java OOP principles and concurrency control.
mediumFinancial and E-Commerce OOPFreq: medium
Prerequisites:OOP BasicsFinite State Machines
⚙️
Functional Requirements
  • Users can create multiple accounts with unique IDs.
  • Users can deposit and withdraw money from their accounts.
  • Users can transfer money between their own or other users' accounts.
  • All transactions must be recorded in a ledger for audit.
  • Statements can be generated for any account showing transaction history.
  • Transactions must be atomic and thread-safe to prevent race conditions.
  • Support transaction states: PENDING, COMPLETED, FAILED.
👤
Use Cases
  • Create Account
  • Deposit Money
  • Withdraw Money
  • Transfer Money Between Accounts
  • Generate Account Statement
  • Record Transaction in Ledger
🧑‍💻
Actors
  • User
  • System Administrator
  • External Payment Gateway
❓
Clarifying Questions to Ask
  • How many concurrent threads or users are expected to access the wallet system simultaneously?
  • Should the system support scheduled or recurring transactions?
  • What is the pricing or fee strategy for transfers or transactions?
  • How should the system handle insufficient balance during withdrawal or transfer?
  • Are partial failures allowed in multi-step transactions?
  • Is eventual consistency acceptable for ledger updates or must it be strictly consistent?
  • Should the statement generation support filtering by date range or transaction type?
Out of Scope
✗ Integration with external banking systems.
✗ Currency conversion and multi-currency support.
✗ Fraud detection mechanisms.
✗ User authentication and authorization.
⚡
QPS Calculation
read qps1000
write qps500
🖥️
Server Estimate
application servers5
database servers3
🔢
Enumerations
enum TransactionStatus
PENDING | COMPLETED | FAILED
Defines finite states of a transaction to enforce state machine behavior and prevent invalid states. Using enum prevents typo bugs and enables exhaustive switch-case handling.
enum TransactionType
DEPOSIT | WITHDRAWAL | TRANSFER
Categorizes transactions by type to enable type-specific processing and reporting. Enum ensures type safety and clear semantics.
enum AccountType
SAVINGS | CURRENT
Distinguishes account categories for business logic differentiation. Enum prevents invalid account type assignments.
📦
Account
4 fields
private final String accountIdprivate final AccountType accountTypeprivate volatile double balanceprivate final List<Transaction> transactions
Represents a user wallet account holding balance and transaction history. Manages balance updates atomically and provides thread-safe access to transactions.
public String getAccountId()Returns the unique identifier of the account.
public synchronized double getBalance()Returns the current balance in a thread-safe manner.
public synchronized void credit(double amount)Atomically adds amount to balance and records a deposit transaction.
public synchronized boolean debit(double amount)Atomically subtracts amount from balance if sufficient funds exist, records withdrawal transaction, returns success status.
public synchronized void addTransaction(Transaction transaction)Adds a transaction to the account's transaction list in a thread-safe way.
public synchronized List<Transaction> getTransactions()Returns a copy of the transaction list to avoid concurrent modification.
📦
Transaction
7 fields
private final String transactionIdprivate final TransactionType typeprivate final double amountprivate volatile TransactionStatus statusprivate final Date timestampprivate final String sourceAccountIdprivate final String destinationAccountId
Represents a financial operation with immutable core data and volatile status to reflect lifecycle changes. Tracks source and destination accounts for transfers.
public String getTransactionId()Returns the unique transaction identifier.
public synchronized TransactionStatus getStatus()Returns the current status of the transaction in a thread-safe manner.
public synchronized void setStatus(TransactionStatus status)Updates the transaction status atomically.
📦
Transfer
3 fields
private final Transaction transactionprivate final Account sourceAccountprivate final Account destinationAccount
Encapsulates a transfer operation as a specialized transaction involving two accounts. Coordinates debit and credit atomically.
public synchronized boolean execute()Performs atomic debit from source and credit to destination, updates transaction status, returns success.
📦
Ledger
1 fields
private final List<Transaction> transactions
Maintains an immutable audit log of all transactions system-wide. Supports thread-safe append and query operations.
public synchronized void recordTransaction(Transaction transaction)Appends a transaction to the ledger atomically.
public synchronized List<Transaction> getTransactions()Returns a copy of all recorded transactions.
📦
Statement
4 fields
private final Account accountprivate final Date fromDateprivate final Date toDateprivate final List<Transaction> filteredTransactions
Generates a filtered view of transactions for an account within a date range. Provides immutable snapshot for reporting.
public void generate()Filters account transactions by date range and stores them.
public List<Transaction> getFilteredTransactions()Returns the filtered list of transactions.
🔗
Relationships
Account◆──►Transaction[1 to many]
compositionAccount owns Transactions; Transactions cannot exist without Account
🗄️
Accounts
PK: accountId
accountId STRINGaccountType STRINGbalance DOUBLE
🗄️
Transactions
PK: transactionId
transactionId STRINGtype STRINGamount DOUBLEstatus STRINGtimestamp TIMESTAMPsourceAccountId STRINGdestinationAccountId STRING
🔌
CreateAccount
Request
{
  "userId": "String",
  "accountType": "AccountType"
}
Response
{
  "accountId": "String",
  "status": "String"
}
Creates a new account for a user with specified type.
🔌
TransferMoney
Request
{
  "sourceAccountId": "String",
  "destinationAccountId": "String",
  "amount": "double"
}
Response
{
  "transactionId": "String",
  "status": "TransactionStatus"
}
Transfers money atomically between two accounts.
🔌
GenerateStatement
Request
{
  "accountId": "String",
  "fromDate": "Date",
  "toDate": "Date"
}
Response
{
  "transactions": "List<Transaction>"
}
Generates account statement filtered by date range.

Practice

(1/5)
1. In designing the User and Message classes for the messaging system, should User be an abstract class or an interface, and why?
easy
A. User should be an interface to allow multiple inheritance of user behaviors.
B. User should be an interface because it only defines behavior without state.
C. User should be an abstract class because it holds common user state like userId and username.
D. User should be an abstract class to enforce implementation of all methods in subclasses.

Solution

  1. Step 1: Analyze if User has state or just behavior

    User holds common state such as userId and username, which requires fields and possibly default implementations.
  2. Step 2: Determine if abstract class or interface fits state and behavior

    Abstract class supports shared state and partial implementation, while interface cannot hold instance state.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    User has common state -> abstract class fits better than interface [check]
Hint: Stateful base class -> abstract class, stateless -> interface [check]
Common Mistakes:
2. In designing the ESPNcricinfo system, should the Ball entity be modeled as an abstract class or an interface? Consider that Ball has state like ballNumber and behavior like calculateRuns(). Which is the best choice and why?
easy
A. Abstract class, because Ball has shared state and behavior that subclasses inherit.
B. Interface, because Ball only defines behavior and no state should be stored.
C. Interface, because Java interfaces can now have default methods with state variables.
D. Abstract class, because it allows multiple inheritance of Ball features.

Solution

  1. Step 1: Analyze Ball's state and behavior

    Ball has state (ballNumber) and behavior (calculateRuns), so it needs to store data and provide common logic.
  2. Step 2: Abstract class vs interface for state

    Interfaces cannot hold instance state; abstract classes can hold state and provide shared implementation.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Stateful entity with behavior -> abstract class [check]
Hint: Stateful with behavior -> abstract class, stateless behavior -> interface [check]
Common Mistakes:
3. To add a WeekendFeeStrategy for locker usage fees in the current Amazon Locker design, which minimal change respects SOLID principles?
medium
A. Modify the existing FeeCalculator class to add weekend fee logic inside the calculateFee() method.
B. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without changing existing classes.
C. Add weekend fee logic directly inside the Locker class to calculate fees based on current day.
D. Add a boolean isWeekend flag to Package and modify LockerManager to apply extra fees conditionally.

Solution

  1. Step 1: Identify the SOLID principle involved

    Open-Closed Principle (OCP) requires adding new behavior without modifying existing code.
  2. Step 2: Evaluate options for minimal change respecting OCP

    Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without changing existing classes. suggests adding a new strategy class, but injecting it requires modifying existing FeeCalculator or client code, violating minimal change. Add a boolean isWeekend flag to Package and modify LockerManager to apply extra fees conditionally. adds a flag and conditional logic, which is a minimal change localized to Package and LockerManager, preserving OCP better than modifying FeeCalculator or Locker.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Minimal change with conditional flag -> respects OCP [check]
Hint: OCP: add new behavior without modifying existing classes [check]
Common Mistakes:
4. The brokerage system currently calculates fees using a FeeCalculator class. To add a new WeekendFeeStrategy without modifying existing code, which SOLID principle applies and what is the minimum change required?
medium
A. Open/Closed Principle: Create a new WeekendFeeStrategy class implementing FeeStrategy interface; FeeCalculator uses polymorphism.
B. Single Responsibility Principle: Modify FeeCalculator to handle weekend fees explicitly.
C. Dependency Inversion Principle: Inject concrete WeekendFeeStrategy directly into FeeCalculator constructor.
D. Liskov Substitution Principle: Replace FeeCalculator with WeekendFeeCalculator subclass overriding fee logic.

Solution

  1. Step 1: Identify the SOLID principle for extensibility

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: How to apply OCP here

    Create a new class WeekendFeeStrategy implementing FeeStrategy interface; FeeCalculator uses polymorphism to apply different strategies without modification.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class + polymorphism -> OCP compliance [check]
Hint: Add new fee strategy class, no FeeCalculator changes [check]
Common Mistakes:
5. 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:
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