using System.Collections.Generic;
using System.Threading.Tasks;
public static class BankSample
public static BankDbContext Database { get; } = new BankDbContext();
public static async Task Main()
var bankSampleTests = new BankSampleTests();
await bankSampleTests.RunTests();
var owner = new Owner(Guid.NewGuid());
var checkingAccount = SetupAccount(owner, BankAccountType.Checking, 325);
var savingsAccount = SetupAccount(owner, BankAccountType.Checking, 100);
Console.WriteLine(checkingAccount != null && savingsAccount != null
? "Accounts set up successfuly"
: "Error setting up accounts");
private static async Task<IBankAccount> SetupAccount(Owner owner, BankAccountType accountType, double initialDeposit)
BankAccount account = new BankAccount(Guid.NewGuid(), owner.Id, accountType);
await account.DepositAsync(initialDeposit);
if (ex is ArgumentException)
private static void LogError(Exception ex)
Console.WriteLine($"WARNING: {ex.Message}. StackTrace: {ex.StackTrace}");
public class BankAccount : IBankAccount
public BankAccount(Guid id, Guid ownerId, BankAccountType accountType, double initialBalance = 0)
this.AccountType = accountType;
this.Balance = initialBalance;
public async Task<ITransaction> DepositAsync(double amount)
throw new ArgumentException("amount", "Amount must be greater than zero.");
var transaction = new Transaction(this.OwnerId, this.Id, amount);
await BankSample.Database.Transactions.Add(transaction);
await BankSample.Database.SaveChangesAsync();
public async Task<ITransaction> WithdrawAsync(double amount)
throw new ArgumentException(nameof(amount), "Amount must be greater than zero.");
if (!this.AllowOverdraft && this.Balance + - amount < 0)
throw new OverdraftException(this.Balance - amount);
var transaction = new Transaction(this.OwnerId, this.OwnerId, amount);
await BankSample.Database.Transactions.Add(transaction);
public Guid OwnerId { get; }
public double Balance { get; private set; }
public double InterestRate { get; private set; }
public bool IsOverdrawn => this.Balance < 0;
public bool AllowOverdraft { get; set; }
public BankAccountType AccountType { get; }
public class Transaction : ITransaction
public Transaction(Guid ownerId, Guid accountId, double amount)
this.AccountId = accountId;
public Guid OwnerId { get; }
public Guid AccountId { get; }
public DateTime Time { get; }
public double Amount { get; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public class OverdraftException : Exception
public OverdraftException(double overdraftAmount)
this.OverdraftAmount = overdraftAmount;
public double OverdraftAmount { get; private set; }
public enum BankAccountType
public class BankDbContext
this.BankAccounts = new DbSet<IBankAccount>();
this.Transactions = new DbSet<ITransaction>();
this.Owners = new DbSet<IOwner>();
public DbSet<IBankAccount> BankAccounts { get; private set; }
public DbSet<ITransaction> Transactions { get; private set; }
public DbSet<IOwner> Owners { get; private set; }
public async Task SaveChangesAsync()
public class DbSet<TEntity>
private IList<TEntity> entities = new List<TEntity>();
public async Task Add(TEntity entity)
this.entities.Add(entity);
public interface IBankAccount
double InterestRate { get; }
BankAccountType AccountType { get; }
bool IsOverdrawn { get; }
bool AllowOverdraft { get; set; }
Task<ITransaction> DepositAsync(double amount);
Task<ITransaction> WithdrawAsync(double amount);
public interface ITransaction
string FirstName { get; set; }
string LastName { get; set; }
public class BankSampleTests
public async Task RunTests()
var bankAccountTests = new BankAccountTests();
await bankAccountTests.RunTests();
public class BankAccountTests
public async Task RunTests()
Console.WriteLine("Running tests...");
this.Initial_Balance_Test();
this.Deposit_Invalid_Test();
await this.Deposit_Balance_Test();
await this.Deposit_Transaction_Test();
public void Initial_Balance_Test()
var owner = new Owner(Guid.NewGuid());
var account = new BankAccount(Guid.NewGuid(), owner.Id, BankAccountType.Checking);
account.Should().NotBeNull();
account.Balance.Should().Be(0);
Console.WriteLine($"{nameof(Initial_Balance_Test)} => Create a new account, balance should be 0. Balance: {account.Balance}");
public void Deposit_Invalid_Test()
var owner = new Owner(Guid.NewGuid());
var account = new BankAccount(Guid.NewGuid(), owner.Id, BankAccountType.Checking);
Func<Task> func = async () => await account.DepositAsync(-1);
func.Should().ThrowAsync<ArgumentException>();
Console.WriteLine($"{nameof(Deposit_Invalid_Test)} => Create a new account, deposit an invalid amount, should throw exception. ");
public async Task Deposit_Balance_Test()
var owner = new Owner(Guid.NewGuid());
var account = new BankAccount(Guid.NewGuid(), owner.Id, BankAccountType.Checking);
await account.DepositAsync(500);
account.Should().NotBeNull();
account.Balance.Should().Be(500);
Console.WriteLine($"{nameof(Deposit_Balance_Test)} => Create a new account, with a deposit of 500, balance should be 500. Balance: {account.Balance}");
public async Task Deposit_Transaction_Test()
var owner = new Owner(Guid.NewGuid());
var account = new BankAccount(Guid.NewGuid(), owner.Id, BankAccountType.Checking);
var transaction = await account.DepositAsync(500);
account.Should().NotBeNull();
account.Balance.Should().Be(500);
transaction.Should().NotBeNull();
transaction.Amount.Should().Be(500);
Console.WriteLine($"{nameof(Deposit_Transaction_Test)} => Create a new account, with a deposit of 500, transaction amount should be 500. Transaction Amount: {transaction.Amount}");