A modular banking system demonstrating core Object-Oriented Programming (OOP) principles and SOLID design patterns. Built with clean architecture separation between business logic, data access, and presentation layers. Focuses on maintainability, extensibility, and type-safe transaction handling with event-driven architecture.
- Account Management: Create, suspend, activate, and deactivate accounts (Credit/Debit)
- Customer Management: Register customers with multiple account associations
- Transaction Operations: Deposit, withdrawal, and transfer with validation
- Balance Tracking: Real-time balance updates with account limit enforcement
- Transaction History: Query transactions by account or customer with persistent storage
- Input Validation: Account status checks, balance verification, limit enforcement
- Exception Handling: Null checks, insufficient funds, inactive account detection
- Event-Driven Architecture: Transaction completion events with custom event args
The system follows a layered architecture with strict separation of concerns:
BankingSystem/
├── Core/ # Domain entities and business rules
│ ├── Entities/ # Account, Customer, Transaction abstractions
│ └── Events/ # Event handling for transaction lifecycle
├── Services/ # Business logic orchestration
│ ├── AccountService
│ ├── CustomerService
│ └── TransactionService
├── Data/ # Data access layer with repository pattern
│ ├── IRepository<T>
│ ├── AccountRepository
│ ├── CustomerRepository
│ └── TransactionRepository
└── ConsoleUI/ # Presentation layer
MainApp/
└── Program.cs # Dependency injection & bootstrapping
- Abstract Base Classes:
AccountandTransaction<TEventArgs>provide polymorphic behavior for account types and transaction types - Interface Segregation: Separate service interfaces (
IAccountService,ICustomerService,ITransactionService) for independent operations - Repository Pattern: Generic
IRepository<T>abstracts data storage, enabling future persistence implementations (database, file system) - Event-Driven Transactions: Transactions trigger
TransactionCompletedevents for logging, auditing, or notification extensions - Dependency Injection: Services accept repository interfaces via constructor injection, enabling testability and flexibility
- Private setters on
IDproperties prevent external modification Balanceprotected setter ensures onlyAccountmethods modify stateAccountscollection inCustomermanaged throughAssignAccount()method
Accountabstract class defines contract for all account types withAccLimitExceeded()abstract methodITransactioninterface standardizes transaction behavior (Execute(), status tracking)Transaction<TEventArgs>generic base class provides template for transaction implementation
CreditAccountandDebitAccountinherit fromAccountwith specialized limit logicDepositeTransaction,WithdrawTransaction,TransferTransactioninherit fromTransaction<TEventArgs>- Repository implementations (
AccountRepository,CustomerRepository,TransactionRepository) implementIRepository<T>
Account.AccLimitExceeded()overridden by derived classes for different business rulesTransaction.Execute()polymorphic behavior for deposit, withdrawal, transfer operationsIRepository<T>allows service layer to work with any repository implementation
Account: Manages balance and account state onlyTransaction: Handles transaction execution logic- Service Classes: Each service focuses on one entity type (Account, Customer, Transaction)
- Repository Classes: Only responsible for data storage/retrieval
- New account types can be added by extending
Accountwithout modifying existing code - New transaction types extend
Transaction<TEventArgs>(system extensible for loan payments, fees, etc.) - Event system allows adding observers without modifying transaction logic
- All
Accountderivatives (CreditAccount,DebitAccount) can substitute base class - Any
ITransactionimplementation works interchangeably inTransactionService - Repository pattern ensures any
IRepository<T>implementation is substitutable
- Separate interfaces for services (
IAccountService,ICustomerService,ITransactionService) - Clients depend only on the interface methods they use
IRepository<T>provides minimal CRUD operations without forcing unnecessary methods
- Services depend on
IRepository<T>abstraction, not concrete implementations Program.csinjects repository implementations into services- High-level modules (services) don't depend on low-level modules (repositories)
- Generic
IRepository<T>interface with concrete implementations for each entity - Abstracts data access from business logic
- Enables future migration to database storage (Entity Framework, Dapper)
Transaction<TEventArgs>defines transaction lifecycle withExecute()abstract method- Derived classes implement specific execution logic while inheriting common behavior
- Event system (
TransactionCompleted) allows decoupled notification of transaction outcomes - Services subscribe to transaction events for logging/auditing without tight coupling
- Language: C# 12
- Framework: .NET 8
- Application Type: Console Application
- Architecture: Layered (Core/Services/Data/UI separation)
-
Clone the repository:
git clone <repository-url> -
Open the solution in Visual Studio 2022 or later
-
Build the solution:
- Right-click solution → Build Solution
- Or press
Ctrl+Shift+B
-
Run the
MainAppproject:- Set
MainAppas startup project if not already - Press
F5to run
- Set
- BankingSystem.csproj: Core library containing domain models, services, and data layer
- MainApp.csproj: Entry point application with dependency wiring and UI initialization