4

I am using EF Code First and I had a model like below.

public class Account
{
    [Required]
    public string AccountNo { get; set; }
    [Required]
    public decimal Balance { get; set; }
}

I used a Service class to withdraw and deposit amount to Account. Then I came across this and realised that Account is Anemic. So I happily added Withdraw and Deposit methods to Account.

public class Account
{
    [Required]
    public string AccountNo { get; set; }
    [Required]
    public decimal Balance { get; set; }
    public void Withdraw(decimal amount)
    {
        // TODO: Check if enough balance is available
        Balance -= amount; 
    }
    public void Deposit(decimal amount)
    {
        Balance += amount; 
    }

}

I was very happy with this until I realised that this does not implement encapsulation correctly. You should not be able to modify the Balance directly - you should use Withdraw and Deposit methods. But in my solution you can modify the Balance directly.

But I cannot make Balance a readonly property as I am using EF code first approach and readonly properties won't result in the column being created in the database table.

I thought of leaving Account as anemic and create a higher level Account class which implements encapsulation and Withdraw/Deposit methods. But in that case the higher level class would not have validation data annotations and I won't get the free functionality provided by the framework.

What is the solution?

  • Entity framework has to use reflection and to reflection it does not matter if it's private or public. So if you are making something public just because you think entity framework won't be able to use it otherwise - don't. – Esailija Aug 11 '13 at 12:41
  • 2
    public decimal Balance { get; protected set; } should fix your problem. – mortalapeman Aug 11 '13 at 16:25
6

There is a fundamental difference between your Data Access Object (DAO) and your Business Object (BO). Your DAO is a one-to-one mapping of what your data store looks like. In this case, for your AccountDAO object, the AccountNo and Balance properties will have getters and setters. Your Account object - the business layer objects - will not look the same as your DAO.

// This is a DAO
public class AccountDAO {
   public string AccountNo { get; set; }
   public decimal Balance { get; set; }
}

// This is a business object
public class Account {
   private readonly string accountNo;
   private readonly decimal openingBalance;
   private readonly List<decimal> adjustments; // This should probably be a list of transactions.

   public class Account(string accountNo, decimal openingBalance) {
      this.accountNo = accountNo;
      this.openingBalance = openingBalance;
      this.adjustments = new List<decimal>();
   }

   public string AccountNo {
      get { return accountNo; }
   }

   public decimal Balance {
      get { return openingBalance + SumOfAdjustments(); }
   }

   public void Deposit(decimal amount) {
      adjustments.Add(amount);
   }

   public void Withdraw(decimal amount) {
      decimal testBalance = Balance - amount;
      if (testBalance < 0.0m)
         throw new AccountBalanceException("Account Balance for " + accountNo + " is less than $0.00.");

      adjustments.Add(-1.0m * amount);
   }

   private decimal SumOfAdjustments() {
      decimal sum = 0.0m;
      foreach (var adjustment in adjustments) {
         sum += adjustment;
      }
      return sum;
   }
}

// Let's come up with a good way of performing actions on an account.    
public class CashDepositActivity {
   private readonly Account account;
   private readonly decimal amount;

   public CashDepositAction(Account account, decimal amount) {
      this.account = account;
      this.amount = amount;
   }

   public void ExecuteAction() {
      // You should probably have some logging here.
      account.Deposit(amount);
   }
}

public class BalanceTransferActivity {
   private readonly Account sourceAccount;
   private readonly Account targetAccount;
   private readonly decimal amount;

   public BalanceTransferActivity(Account sourceAccount, Account targetAccount, decimal amount) {
      this.sourceAccount = sourceAccount;
      this.targetAccount = targetAccount;
      this.amount = amount;
   }

   public void ExecuteAction() {
      sourceAccount.Withdraw(amount);
      targetAccount.Deposit(amount);
   }  
}

This is just an example, of course, and it is by no means an end-all solution. I know tools like EF have myriad examples where the data object, business object, and view/edit object are all the same object, passed from the database to view. In my experience, that works great on small projects where there is not a lot of business logic. However, it breaks down as your business rules and access restrictions grow in complexity.

  • 1
    +1 You might want to add that DDD explicitly says that domain entities should be persistant ignorant. And the entity specified by the OP is obviously not, thus breaking one of the fundemental rules of DDD. – jgauffin Aug 22 '13 at 15:41
0

The way in which you describe it, the Balance of an Account is the result of all the Transactions applied to that Account. In this case Balance is a read-only property because you would modify it by appending a Transaction to the list attached to the relevant account. So perhaps an additional Transaction class would get you what you want.

It is also normal to have smaller, simpler classes in your Data Access Layer that are composed by classes in the Business Layer. If you need a read-only value that could be loaded from a database, then having a Business Layer object that can be constructed from your Data Access Layer, but doesn't then expose it publicly is a perfectly fine way to go. Alternatively you can have Mappers that convert Data Access Layer classes to Business Layer classes and vice versa.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.