A banking console application using SOLID principles, Clean Architecture, Domain Driven Design and Test Driven Development in C#.
Project Dependencies Flow:
ConsoleApp → Infrastructure → Application → Domain
To run the application in command prompt:
dotnet run --project AwesomeGICBank.ConsoleApp
- Architecture Overview
- Domain Model
- Application Layer
- Infrastructure Layer
- Console Application Layer
- Testing
- Key Features
- Design Patterns Used
- SOLID Principles Application
The application follows Clean Architecture and Domain-Driven Design principles, with a clear separation of concerns across four main layers:
- Core business logic and entities
- No dependencies on other layers
- Contains value objects, aggregates, and domain exceptions
- Use cases that orchestrate domain objects
- Defines repository interfaces
- Contains commands and results
- Implements repository interfaces
- Provides concrete implementations of system services
- Contains in-memory data persistence
- User interface implementation
- Dependency injection configuration
- Input parsing and command handling
- Properties: Id, Balance, Transactions
- Methods:
- Deposit
- Withdraw
- GetBalance
- GetTransactions
- Enforces invariants like preventing negative balances
- Immutable representation of monetary amounts
- Validates non-negative values
- Supports basic operations:
- Add
- Subtract
- Maintains precise decimal calculations
- Properties:
- Id
- Date
- AccountId
- Type
- Amount
- ResultingBalance
- Types:
- Deposit
- Withdrawal
- Interest
- Factory methods for creating different transaction types
- Properties:
- EffectiveDate
- RuleId
- Rate
- Validates interest rates between 0-100%
- Calculates daily interest
The domain layer implements robust validation through custom exceptions:
InvalidAccountIdExceptionInvalidMoneyExceptionInsufficientFundsExceptionInvalidInterestRateExceptionInvalidTransactionIdException
- Handles deposits and withdrawals
- Validates inputs
- Updates account balance
- Returns transaction results
- Creates new interest rules
- Validates rule parameters
- Maintains rule history
- Generates monthly account statements
- Calculates interest
- Combines transactions and interest calculations
public interface ITransactionRepository
{
Task<Account?> GetAccountAsync(AccountId id);
Task SaveAccountAsync(Account account);
Task<IEnumerable<Transaction>> GetTransactionsForPeriodAsync(
AccountId accountId,
DateTime startDate,
DateTime endDate);
}public interface IInterestRuleRepository
{
Task<List<InterestRule>> GetEffectiveRulesAsync(DateTime startDate, DateTime endDate);
Task SaveRuleAsync(InterestRule rule);
Task<InterestRule?> GetRuleByIdAsync(string ruleId);
Task<List<InterestRule>> GetAllRulesAsync();
}- Uses
Dictionary<string, Account>for storage - Implements transaction retrieval and storage
- Maintains account state
- Stores interest rules in memory
- Handles rule effective dates
- Manages rule versions
- Main application loop
- Menu display and navigation
- Command routing
- Processes user input
- Formats output
- Handles command execution
TransactionCommandParserInterestRuleCommandParserStatementCommandParser
- Uses Microsoft.Extensions.DependencyInjection
- Configures scoped and singleton services
- Separates infrastructure and application services
The application includes unit tests covering:
- Account operations
- Money calculations
- Transaction creation
- Interest rule validation
- Transaction processing
- Interest calculation
- Statement generation
[Fact]
public void Withdraw_WithInsufficientFunds_ShouldThrowException()
{
var account = new Account(AccountId.From("AC001"));
var transactionDate = new DateTime(2023, 06, 26);
account.Deposit(transactionDate, Money.FromDecimal(50.00m));
var exception = Assert.Throws<InsufficientFundsException>(() =>
account.Withdraw(transactionDate, Money.FromDecimal(60.00m)));
Assert.Contains("Insufficient funds", exception.Message);
}- Deposits and withdrawals
- Balance tracking
- Transaction history
- Rule-based interest rates
- Daily balance calculation
- Monthly interest accrual
- Monthly account statements
- Transaction listing
- Interest calculation
- Input validation
- Business rule enforcement
- Error handling
- Abstracts data access
- Enables testing
- Supports different storage implementations
- Encapsulates operations
- Separates command data from execution
- Supports validation
- Creates domain objects
- Enforces invariants
- Centralizes object creation
- Immutable objects
- Encapsulated validation
- Business logic containment
- Each class has a single purpose
- Clear separation of concerns
- Focused component responsibilities
- Extensible design
- New features through extension
- Minimal modification risk
- Interface-based design
- Consistent behavior
- Polymorphic implementations
- Focused interfaces
- Minimal dependencies
- Clear contracts
- Dependency injection
- Abstract dependencies
- Flexible configuration