Transactional Architecture

Viewed 57

I had an interview question to design a small personal accounting application to record bank account activities. I have account data model like the following:

public class AccountModel
{
    public string Name { get; set; }
    public string AccountNumber { get; set; }
    public AccountType Type { get; set; }  // checking, saving, credit, etc
    public decimal Balance { get; set; }

    public bool Deposit(decimal amount, string description, DateTime date, ref string error)
    {
        Balance += amount;
        return true;
    }

    public bool Withdraw(decimal amount, string description, DateTime date, ref string error)
    {
        //do something
        return true;
    }

    public bool Transfer(decimal amount, string description, DateTime date, AccountModel targetAccount, ref string error)
    {
        // do something
        return true;
    }
}

public class AccountTransactionModel
{
    public enum TranslactionType
    {
        Deposit,
        Withdraw,
        Transfer,
    }
    public long TransactionId { get; set; }
    public DateTime Date { get; set; }
    public decimal Amount { get; set; }
    public string Description { get; set; }
    public TranslactionType Type { get; set; }
    public AccountModel Account { get; set; }
}

Using the repository pattern, I have repository interface and implementation like the following:

public interface IAccountOpInterface
{
    bool CreateAccount(string name, string accountNumber, AccountModel.AccountType type, decimal initialBalance);
    List<AccountModel> GetAccounts();
    AccountModel GetAccount(long accountId);
    bool Deposit(AccountModel account, decimal amount, string description, DateTime date, ref string error);
    bool Withdraw(AccountModel account, decimal amount, string description, DateTime date, ref string error);
    public bool Transfer(AccountModel fromAccount, decimal amount, string description, DateTime date, AccountModel toAccount, ref string error);
    List<AccountTransactionModel> RunQuery(Query query);

    bool Load();
    bool Save();
    void CreateTransaction(AccountTransactionModel accountTransactionModel);
}

Then the interviewer said I'm using "Transactional Architecture", which is not a good design. All of the operations should be placed in the data model instead of the repo class.

I'm a little bit confused about the responsibility between the Repository class and the Data Model. I thought the account model should be in charge of the customized operation of Deposit, Withdraw and Transfer operations for the specific account type.

and the repo class should be in charge of

  1. Call the data model to perform Deposit, Withdraw, Transfer
  2. Record the previous operation as a transaction.

The business logic layer should call the repo class to perform the all operations.

Is my understanding correct? I probably missed some design pattern document regarding "Transactional Architecture". But I googled with no findings. Can anyone share me a link for this specific topic?

1 Answers

According to Martin Fowler, the Repository Pattern Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects. In this example. If Account and AccountTransaction are the data models. Then you need 2 repositories, AccountRepository and AccountTransaction to handle the collections of AccountModel and AccountTransactionModel

public class AccountRepository
{
    AccountModel Get(int id);
    IEnumerable<AccountModel> GetAll();
    void Add(AccountModel account);
}

public class AccountTransactionRepository
{
    AccountTransactionModel Get(int id);
    IEnumerable<AccountTransactionModel> GetAll();
    void Add(AccountTransactionModel account);
}

Other collection operations like Update and Remove are optional. Save() will not be in the repository pattern. It will be in the UnitOfWork class.

I assume every time when a Deposit, Withdraw, Transfer operation is performed, the account model(s) will be called and a new AccountTransaction object will be created. This is the business logic of this question, and it should stay in the Domain layer, which is above the data access layer.

namespace BusinessLogic
{
    public class AccountOperationFacade
    {
        void Transfer(AccountModel from, AccountMode to, decimal amount, string description, DateTime date)
        {
            from.Withdraw(amount, description, date);
            to.Deposit(amount, description, date);
            _unitOfWork.AccountTransactionRepository.Add(new AccountTransactionModel()
            {
                From = from.Name,
                To = to.Name,
                Amount = amount
                Description = description,
                Date = date
            });
        }
    }
}
Related