Solarion
Honorary Master
- Joined
- Nov 14, 2012
- Messages
- 28,102
- Reaction score
- 17,873
Below is a quick mockup of what I would do as far as a starting point for interfaces and classes. In my mind, I savings account would typically have the same behaviour as a Current account so I would inherit that as a starting point to not rewrite, but in my example I left them completely separate.
I haven't including any samples for the repository itself.
C#:public enum AccountType { Savings = 1, Current = 2 } public interface IAccount { public string AccountNumber { get; } public ICustomer Customer { get; } public decimal Balance { get; } public AccountType AccountType { get; } public void Open(); } public interface ITransactionalAccount : IAccount { public decimal MinimumBalance { get; set; } public void Deposit(decimal amount); public void Withdraw(decimal amount); } public interface IInterestBearingAccount : IAccount { public decimal InterestRate { get; } public decimal CalculateInterest(); } public class CurrentAccount : ITransactionalAccount { private IAccountRepository accountRepository; public string AccountNumber { get; protected set; } public ICustomer Customer { get; protected set; } public AccountType AccountType { get => AccountType.Current; } public decimal Balance { get; protected set; } public decimal MinimumBalance { get; set; } public CurrentAccount(IAccountRepository accountRepository, string accountNumber, ICustomer customer, decimal balance, decimal minimumBalance) { this.accountRepository = accountRepository; this.AccountNumber = accountNumber; this.Customer = customer; this.Balance = balance; this.MinimumBalance = minimumBalance; } public void Deposit(decimal amount) { this.Balance += amount; this.Update(); } public void Withdraw(decimal amount) { decimal newBalance = this.Balance - amount; if (this.MinimumBalance < newBalance) throw new Exception("Insufficient funds"); this.Balance = newBalance; this.Update(); } private void Update() { accountRepository.Update(this); } public void Open() { this.AccountNumber = accountRepository.Create(this); } } public class SavingsAccount : ITransactionalAccount, IInterestBearingAccount { private IAccountRepository accountRepository; public string AccountNumber { get; protected set; } public ICustomer Customer { get; protected set; } public AccountType AccountType { get => AccountType.Current; } public decimal Balance { get; protected set; } public decimal MinimumBalance { get; set; } public decimal InterestRate { get; set; } public SavingsAccount(IAccountRepository accountRepository, string accountNumber, ICustomer customer, decimal balance, decimal minimumBalance, decimal interestRate) { this.accountRepository = accountRepository; this.AccountNumber = accountNumber; this.Customer = customer; this.Balance = balance; this.MinimumBalance = minimumBalance; this.InterestRate = interestRate; } public void Deposit(decimal amount) { this.Balance += amount; this.Update(); } public void Withdraw(decimal amount) { decimal newBalance = this.Balance - amount; if (this.MinimumBalance < newBalance) throw new Exception("Insufficient funds"); this.Balance = newBalance; this.Update(); } private void Update() { accountRepository.Update(this); } public void Open() { this.AccountNumber = accountRepository.Create(this); } public decimal CalculateInterest() { // Calculate interest and return - just used as an example } } // AccountManager / AccountFactory for creating concrete implementations public class AccountManager { private IAccountRepository accountRepository; public AccountManager(IAccountRepository accountRepository) { this.accountRepository = accountRepository; } // The accountRepository Get method can call the factory / manager method public IAccount CreateAccount(AccountType accountType, string accountNumber, ICustomer customer, decimal balance, decimal? minimumBalance, decimal? interestRate) { switch(accountType) { case AccountType.Current: return new CurrentAccount(accountRepository, accountNumber, customer, balance, minimumBalance.Value); case AccountType.Savings: return new SavingsAccount(accountRepository, accountNumber, customer, balance, minimumBalance.Value, interestRate.Value); default: throw new NotImplementedException(); } } public IAccount CreateAccount(AccountType accountType, ICustomer customer, decimal? minimumBalance, decimal? interestRate) { switch(accountType) { case AccountType.Current: return new CurrentAccount(accountRepository, null, customer, 0, minimumBalance.Value); case AccountType.Savings: return new SavingsAccount(accountRepository, null, customer, 0, minimumBalance.Value, interestRate.Value); default: throw new NotImplementedException(); } } } public interface IAccountRepository { public string Create(IAccount account); public void Update(IAccount account); public IAccount Get(string accountNumber, ICustomer customer); } public interface ICustomer { public int CustomerId { get; } } public class Individual : ICustomer { public int CustomerId { get; internal set; } } public class Company : ICustomer { public int CustomerId { get; internal set; } }
There's a lot of questions I have about this design. Say if you are creating a new Savings Account you now have to pass in these parameters.
accountNumber, ICustomer customer, decimal balance, decimal minimumBalance, decimal interestRate
all from some external source whereas in mind balance, minimum balance, interest rate should be pulled from the database depending on the account type, customers balance etc.
The more I think about it the more complicated it gets and the reason is, I find it difficult to separate the Account from the Customer. The Account is an entity in itself which should have only one link to the client, the AccountNo.
Yet every time an account is updated, do you update the Customer too as part of the account's logic or do you have a whole separate Customer class and repository for handling customer crud. I just see confusion!!
Your thoughts would be appreciated!
Last edited: