A console-based Java application that simulates a comprehensive bank management system with support for multiple account types, transaction processing, and role-based access control.
This project demonstrates core object-oriented programming concepts through a banking system that allows users to create accounts, perform financial transactions, and manage customer data. The system distinguishes between regular customers, premium customers, and bank managers, each with different privileges and transaction limits.
src/
├── Main.java # Application entry point
├── com/bank/
│ ├── controllers/
│ │ ├── MenuController.java # Main menu and application flow
│ │ ├── AccountController.java # Account creation and viewing
│ │ └── TransactionController.java # Transaction processing
│ ├── interfaces/
│ │ └── AccountOperations.java # Core banking operations interface
│ ├── models/
│ │ ├── Account.java # Abstract base account class
│ │ ├── SavingsAccount.java # Savings account implementation(extends from Base Account class)
│ │ ├── CheckingAccount.java # Checking account implementation(extends from Base Account class)
│ │ ├── Customer.java # Customer/Manager entity
│ │ ├── Transaction.java # Transaction record
│ │ └── enums/
│ │ ├── AccountType.java # SAVINGS, CHECKING
│ │ ├── CustomerType.java # REGULAR, PREMIUM
│ │ ├── Role.java # CUSTOMER, MANAGER
│ │ └── TransactionType.java # DEPOSIT, WITHDRAW
│ └── repository/
│ ├── AccountManager.java # Account storage and retrieval
│ ├── CustomerManager.java # Customer storage
│ └── TransactionManager.java # Transaction history management
- Supported Account Types: Savings and Checking accounts
- Customer Types: Regular and Premium customers
- Auto-generated IDs: Accounts receive unique UUID-based account numbers
- Minimum Deposit Requirements:
- Premium customers: $10,000 minimum initial deposit
- Regular customers: No minimum
Key Classes:
AccountController.java- Handles account creation workflowAccount.java- Abstract base class with common account propertiesSavingsAccount.java- Savings-specific implementationCheckingAccount.java- Checking-specific implementation
- Customer View: View individual account details by account number
- Manager View: View all accounts in the system (requires Manager ID starting with "MGR")
- Account Summary Display: Includes account number, customer name, type, balance, and status
- Interest Rate: 3.5% annual (applied monthly)
- Minimum Balance: $500 (enforced for regular customers)
- Premium Benefit: No minimum balance requirement for premium customers
- Interest Calculation:
monthlyInterest = balance × (0.035 / 12)
NB: These features were not implement in the code since this is a console project and is short lived
- Overdraft Limit: $1,000 (allows negative balance up to limit)
- Monthly Fee: $10 (waived for premium customers)
- Overdraft Protection: Allows withdrawals beyond current balance
- Fee Structure: Premium customers exempt from monthly fees
- Deposits: Add funds to account with instant processing
- Withdrawals: Remove funds with validation checks
- Unique Transaction IDs: Format
TXN{timestamp} - Transaction Confirmation: Displays preview before processing
- Status Tracking: PENDING, COMPLETED, FAILED
- Balance Validation: Prevents insufficient fund withdrawals
- Transaction History: Complete audit trail with timestamps
Transaction Flow:
- Account lookup by account number
- Transaction type selection (deposit/withdrawal)
- Amount entry and validation
- Confirmation display with current/new balance
- User confirmation (Y/N)
- Transaction execution and recording
-
Regular Customers:
- Transaction limit: $10,000
- Subject to minimum balance requirements
- Subject to monthly fees on checking accounts
-
Premium Customers:
- Transaction limit: $50,000
- Minimum initial deposit: $10,000
- No minimum balance requirements on savings
- Waived monthly fees on checking accounts
-
Customer (CUSTOMER):
- Can view own accounts
- Can perform transactions within limits
- Auto-assigned ID format:
CUST{5-digit number}
-
Manager (MANAGER):
- Can view all accounts
- Can view all transaction history
- Unlimited transaction limit
- Auto-assigned ID format:
MGR{5-digit number}
- Storage: Array-based with dynamic resizing
- Initial Capacity: 50 accounts
- Operations:
addAccount()- Add new accountfindAccount()- Retrieve by account numberviewAllAccounts()- Display formatted account listinggetTotalBalance()- Calculate total bank balance
- Storage: Array-based customer repository
- Auto-resizing: Doubles capacity when full
- Customer Tracking: Maintains customer records
- Storage: Array-based transaction log (initial: 100 transactions)
- Operations:
addTransaction()- Record new transactionviewAllTransactions()- Manager-level viewviewTransactionsByAccount()- Account-specific history
This project implements a layered architecture that separates concerns into distinct layers, making the code maintainable, testable, and scalable. The architecture follows a modified MVC (Model-View-Controller) pattern adapted for console applications.
Responsibility: Represents the domain entities and business logic
What Models Do:
- Define the data structure and state of business entities
- Encapsulate business rules and validation logic
- Contain no knowledge of how data is stored or presented
- Implement core business operations (e.g.,
withdraw(),deposit())
Key Models:
Account.java- Abstract base class for all accounts with common properties and operationsSavingsAccount.java/CheckingAccount.java- Specific account implementations with unique rulesCustomer.java- Represents customer/manager entities with role-based attributesTransaction.java- Immutable transaction records with status tracking
Example: SavingsAccount enforces minimum balance rules - this is business logic that belongs in the model, not in controllers or repositories.
// Business logic in the model
public void withdraw(double amount) {
if ((currentBalance - amount) >= MIN_BALANCE) {
super.withdraw(amount);
} else {
// Business rule enforcement
}
}Responsibility: Handles user interaction and application flow
What Controllers Do:
- Receive user input from the console
- Validate and sanitize input data
- Coordinate between repositories and models
- Control application workflow and navigation
- Format and display output to users
- Do NOT contain business logic - delegate to models
Key Controllers:
MenuController.java- Main application flow and menu navigationAccountController.java- Handles account creation and viewing workflowsTransactionController.java- Manages transaction processing workflows
Example: AccountController doesn't know how to store accounts - it delegates to AccountManager:
// Controller coordinates but doesn't implement storage
public void createAccount() {
// 1. Get user input
// 2. Create model object
SavingsAccount newAccount = new SavingsAccount(accountNumber, holder, deposit);
// 3. Delegate storage to repository
accountManager.addAccount(newAccount);
// 4. Display result to user
}Responsibility: Manages data storage, retrieval, and persistence
What Repositories Do:
- Abstract away data storage implementation details
- Provide CRUD operations (Create, Read, Update, Delete)
- Manage collections of domain objects
- Handle data access logic (searching, filtering, aggregating)
- Do NOT contain business logic - only data operations
Key Repositories:
AccountManager.java- Stores and retrieves accountsCustomerManager.java- Manages customer recordsTransactionManager.java- Maintains transaction history
Example: AccountManager handles storage without knowing business rules:
// Repository only handles data operations
public void addAccount(Account account) {
if (accountCount >= accounts.length) {
resizeArray(); // Infrastructure concern
}
accounts[accountCount++] = account; // Storage operation
}Each layer has ONE clear responsibility:
- Models = Business logic and domain rules
- Controllers = User interaction and flow control
- Repositories = Data storage and retrieval
- Changing storage (e.g., from arrays to database) only affects repositories
- Changing business rules only affects models
- Changing UI (e.g., from console to GUI) only affects controllers
- Can test business logic in models independently
- Can test data operations without UI
- Can mock repositories to test controllers
- Easy to add new account types (extend models)
- Easy to add new operations (add controller methods)
- Easy to switch storage mechanisms (modify repositories)
- Models can be used by different controllers
- Repositories can serve multiple controllers
- Same model can have different presentation formats
User Input (Console)
↓
MenuController.start()
↓
AccountController.createAccount()
↓ (gather input & validate)
Create Model: new SavingsAccount(...)
↓ (business logic in model constructor)
AccountManager.addAccount(account)
↓ (storage logic in repository)
Data Stored in Array
↓
Return to Controller
↓
Display Success Message to User
Notice how each layer only talks to adjacent layers:
- Controllers use Repositories and Models
- Models are independent (no dependencies)
- Repositories store Models (no UI knowledge)
Accountabstract class defines template for all account typesAccountOperationsinterface defines contract for account operations- Hiding implementation details while exposing necessary functionality
SavingsAccountandCheckingAccountextendAccountbase class- Inherits common properties: accountNumber, accountHolder, balance
- Demonstrates IS-A relationship (SavingsAccount IS-A Account)
- Method Overriding:
withdraw()method overridden in both account typesSavingsAccount: Enforces minimum balance for regular customersCheckingAccount: Allows overdraft up to limit
- Interface Implementation:
AccountimplementsAccountOperationsinterface - Runtime polymorphism through account type-specific behavior
- Private fields with public getters (e.g.,
accountNumber,balance) - Data hiding through access modifiers
- Controlled access to object state through methods
- Example: Balance can only be modified via
deposit()andwithdraw()
AccountHAS-ACustomer(accountHolder)TransactionHAS-AAccountMenuControllercomposesAccountControllerandTransactionController
- Type-safe constants for:
AccountType(SAVINGS, CHECKING)CustomerType(REGULAR, PREMIUM)Role(CUSTOMER, MANAGER)TransactionType(DEPOSIT, WITHDRAW)TransactionStatus(PENDING, COMPLETED, FAILED)
static int customerCountinCustomerclass ensures unique sequential IDs- Class-level variables shared across all instances
- Used for maintaining system-wide state
- Account constructors initialize common properties
- Subclass constructors call
super()to initialize parent class
Problem: Initially, when initializing arrays inside methods, each method call created new objects, and the array didn't retain previously stored objects.
Solution: Use static keyword or initialize arrays outside methods (in constructors or as instance variables).
Example from the code:
// In Customer.java
private static int customerCount = 0; // Shared across all instances
// In AccountManager.java
private Account[] accounts; // Instance variable
private int accountCount;
public AccountManager() {
this.accounts = new Account[50]; // Initialize in constructor, not in methods
this.accountCount = 0;
}Key Insight:
- Static fields are shared across all class instances (e.g., customer ID counter)
- Instance fields are unique to each object but persist across method calls
- Initializing collections in constructors (not methods) prevents data loss
Implemented dynamic array resizing to handle unlimited growth:
private void resizeArray() {
Account[] newAccounts = new Account[accounts.length * 2];
System.arraycopy(accounts, 0, newAccounts, 0, accounts.length);
accounts = newAccounts;
}Separated validation logic in model classes (e.g., minimum balance checks in SavingsAccount) from controller logic, following Single Responsibility Principle.
Used String.format() for consistent currency and number display:
String.format("$%,.2f", amount) // Outputs: $1,234.56Implemented simple but effective authorization by checking ID prefixes:
if (!userId.startsWith("MGR")) {
System.out.println("✗ Access Denied");
return;
}Implemented preview-confirm workflow to prevent accidental operations, improving user experience and data integrity.
-
Compile the project:
javac Main.java
-
Run the application:
java Main
-
Initial Setup:
- System auto-creates a Manager account on startup
- Manager ID is displayed (format: MGR00001)
- Use this ID to access manager-only features
| Feature | Regular Customer | Premium Customer | Manager |
|---|---|---|---|
| Transaction Limit | $10,000 | $50,000 | Unlimited |
| Savings Min Balance | $500 | None | N/A |
| Checking Monthly Fee | $10 | Waived | N/A |
| View All Accounts | ✗ | ✗ | ✓ |
| View All Transactions | ✗ | ✗ | ✓ |
| Overdraft Limit | $1,000 | $1,000 | N/A |
- Database integration (replace in-memory arrays)
- User authentication and password management
- Interest calculation automation
- Loan and credit features
- Account statements and reports generation
- Multi-currency support
- Transfer between accounts
Basit - basit-devBE
This project is open source and available for educational purposes.