Friday, September 18, 2009

Business Logic Patterns

All of the patterns here are taken from Martin Fowler’s excellent book Patterns of Enterprise Application Architecture.  The following is my interpretation of these patterns and the ramifications of each one.  I highly recommend that you refer back to PoEAA for a full explanation of each pattern.

I presented a session on the first day of TechDays in Vancouver called “Layers: The Secret Language of Architects.”  As part of that presentation we discussed some of the different patterns that are used for coding our business logic.  After the session was over, several people commented that they particularly liked this part of the session and encouraged me to blog about it, so without further ado…

The Patterns

Transaction Script

Transaction script is the simplest of the three patterns.  It is represented by simple procedural code that executes business logic with simple and straight forward programming constructs and does not use object oriented techniques.

Transaction Script should be used when the application has very simple business logic and is not expected to grow much beyond the initial development effort.  It is very quick and easy to get going with Transaction Script because it does not require much in terms of supporting infrastructure.  However, I strongly recommend that if you decided to use Transaction Script that you make explicit seams around it so that when (yes, when, not if) you decide that your application has outgrown the limits of the pattern it will only mean a rewrite of the business logic and not a rewrite of the entire application.  As the complexity of the system grows, the Transaction Script breaks down quite fast.  It causes a lot of duplication and often results in rigid and brittle systems.

Here is a sample of some Transaction Script code.   I’ll be using the canonical example of a funds transfer where a given amount is transferred from one account to another.  The logic is intentionally kept very simple so that it’s easier to talk about the different responsibilities being addressed in the code.

public class FundsTransferService
{
public void TransferFunds(int fromAccountID, int toAccountID, decimal amount)
{
AccountDataAccess dataAccess = new AccountDataAccess();

decimal fromAccountBalance = dataAccess.GetAccountBalance(fromAccountID);
decimal toAccountBalance = dataAccess.GetAccountBalance(toAccountID);

fromAccountBalance -= amount;
toAccountBalance += amount;

dataAccess.SetAccountBalance(fromAccountID, fromAccountBalance);
dataAccess.SetAccountBalance(toAccountID, toAccountBalance);
}
}

The code is very simple and straight forward.  A data access class is used to retrieve the current balance from each account, perform the logic and then save the balances back to the data access class.  Note that of the 7 lines of code in the sample, only 2 are actually business logic.  The rest are infrastructure concerns.

Table Module

Table Module uses one instance to represent all of the rows in a database table.  Each class wraps some representation of the database table (eg. a DataSet) and will pull out a single row to operate on a single item.  The distinction of this pattern is that the business layer is written in an object oriented manner, but instead of storing the data in the objects, the data is stored in the DataSet.

Although DataSets were quite popular in the .NET world, very few architectures made use of the Table Module pattern.  DataSets were usually passed around to represent state, but the object oriented representation of the business logic was not present.  DataSets were instead used in Transaction Script patterns where classes were simply used to group related methods of procedural code.  Table Module does scale in complexity better than Transaction Script because it can take advantage of object oriented techniques, but it still gets increasingly more difficult to implement new functionality because of the heavy infrastructure concerns that remain in the code.

Here is the same logic rewritten in the Table Module pattern.


public class FundsTransferService
{
public void TransferFunds(int fromAccountID, int toAccountID, decimal amount)
{
Account account = Account.Load();
account.TransferFunds(fromAccountID, toAccountID, amount);
account.Save();
}
}

public class Account
{
private const string AccountTable = "Account";
private const string IDColumn = "ID";
private const string BalanceColumn = "Balance";

private readonly DataSet dataSet;

public static Account Load()
{
AccountDataAccess accountDataAccess = new AccountDataAccess();
DataSet dataSet = accountDataAccess.GetAccountTable();
return new Account(dataSet);
}

public Account(DataSet dataSet)
{
this.dataSet = dataSet;
}

public void TransferFunds(int fromAccountID, int toAccountID, decimal amount)
{
DataRow fromAccountRow = GetAccountRow(fromAccountID);
DataRow toAccountRow = GetAccountRow(toAccountID);

decimal fromAccountBalance = (decimal) fromAccountRow[BalanceColumn];
decimal toAccountBalance = (decimal) toAccountRow[BalanceColumn];

fromAccountBalance -= amount;
toAccountBalance += amount;

fromAccountRow[BalanceColumn] = fromAccountBalance;
toAccountRow[BalanceColumn] = toAccountBalance;
}

private DataRow GetAccountRow(int accountID)
{
foreach (DataRow accountRow in dataSet.Tables[AccountTable].Rows)
{
if ((int) accountRow[IDColumn] == accountID)
{
return accountRow;
}
}
return null;
}

public void Save()
{
dataSet.AcceptChanges();
}
}

There is a lot more code in this example, but note how a subclass of Account could override the TransferFunds method if the business logic required specialized logic.  The subclass could reuse all of the infrastructure code and just change the business logic.

Domain Model

Domain Model is an object model that encapsulates both the data and the behaviour.  It takes full advantage of object oriented principles such as encapsulation and polymorphism.  The Domain Model pattern is the best of the three patterns at representing complex domains.  It is the power of isolating the domain from the infrastructure combined with the modeling power of object oriented languages that allows the complexity to scale well when using this pattern.

Business logic implemented with a Domain Model requires significant effort to isolate it from infrastructure concerns.  Because of this additional effort it initially takes longer to develop systems using the Domain Model pattern.  However, due to the powerful methods for representing business logic, it becomes relatively easier (compared to the other patterns that is) to develop as the system grows in complexity.  An initial effort to set up the surrounding infrastructure is rewarded later on by allowing the developers to maintain a constant rhythm and speed of development.

Here is the funds transfer logic as represented using the Domain Model pattern.


public class FundsTransferService
{
public void TransferFunds(Account fromAccount, Account toAccount, decimal amount)
{
fromAccount.Debit(amount);
toAccount.Credit(amount);
}
}

public class Account
{
private decimal balance;

public void Debit(decimal amount)
{
balance -= amount;
}

public void Credit(decimal amount)
{
balance += amount;
}
}

Note the simplicity of this solution and that every single line of code is directly representing business logic.  It is this isolation and focus on business logic that allows it to scale well with complexity.  There is no persistence in this code sample, which is intentional, and persistence techniques will be discussed next.

Persistence in a Domain Model

In the TechDays presentation we presented Active Record and Domain Model as two separate patterns.  This was a conscious diversion from Fowler’s patterns because this is the way that we have observed systems were being built in the wild, that and the fact that none of us had actually ever seen a system that used Table Module.  The prevalence of Active Record tools and frameworks has caused it to be considered a different pattern than Domain Model.  If you go by Fowler’s definition though, Active Record is a persistence pattern of a Domain Model.

Active Record

Active Record uses a one to one mapping between Domain Model classes and tables in the database.  Each class is mapped to a table, each instance is mapped to a row, and each field is mapped to a cell.  Classes are also responsible for loading and saving themselves to the database.

When using Active Record for persistence, we must add some more code to our Domain Model.


public class FundsTransferService
{
public void TransferFunds(int fromAccountID, int toAccountID, decimal amount)
{
Account fromAccount = Account.Load(fromAccountID);
Account toAccount = Account.Load(toAccountID);

fromAccount.Debit(amount);
toAccount.Credit(amount);

fromAccount.Save();
toAccount.Save();
}
}

public class Account
{
private decimal balance;

public void Debit(decimal amount)
{
balance -= amount;
}

public void Credit(decimal amount)
{
balance += amount;
}

public static Account Load(int accountID)
{
// TODO: Implement this method
throw new NotImplementedException();
}

public void Save()
{
// TODO: Implement this method
throw new NotImplementedException();
}
}

This technique combines the responsibility of persistence and business logic.  Note that we have added some persistence code to business logic, so there is some mixing of concerns.  There are several Active Record frameworks that will allow you to remove much of this code from the entities and let the framework handle it, but the concepts remain the same.

Object Relational Mapper

Object Relational Mapper is a pattern that puts a high value on Persistence Ignorance in the Domain Model.  The Domain Model should know nothing about how, or even if, it is persisted to the database.  An Object Relational Mapper is used to map between the Domain Model and the relational database.  Unlike Active Record, the two models can be quite different and take advantage of the powers of each paradigm.  In order to isolate the Domain Model from persistence knowledge, it is usually required to use a Service Facade layer to coordinate the usage of the Object Relational Mapper.

Let’s have a look at the added infrastructure required to use the Object Relational Mapper pattern.


public class FundsTransferFacade
{
private readonly IAccountRepository accountRepository;
private readonly IFundsTransferService fundsTransferService;

public FundsTransferFacade(IAccountRepository accountRepository, IFundsTransferService fundsTransferService)
{
this.accountRepository = accountRepository;
this.fundsTransferService = fundsTransferService;
}

public void TransferFunds(int fromAccountID, int toAccountID, decimal amount)
{
Account fromAccount = accountRepository.Get(fromAccountID);
Account toAccount = accountRepository.Get(toAccountID);

fundsTransferService.TransferFunds(fromAccount, toAccount, amount);
}
}

Here we have added a Service Facade layer that handles the translation into the Domain Model "language". I am also assuming that an Object Relational Mapper is being used, and Aspect Oriented Programming to wrap calls to the Facade Layer which initiates and cleans up the ORM. This is write once code and is not worth showing here.  This is the added infrastructure that is required to get going with a Domain Model, but once it is in place, we can focus more on the business logic.

Summary

We had a look at three difference patterns for representing business logic.  We examined some code samples to illustrate how each pattern will handle increased complexity.  Finally we looked at some of the infrastructure options that we need to implement when using the Domain Model pattern.  I hope this was a valuable exercise, and if not, please leave a comment so that I can improve it.

No comments: