DCI in a different style

After reading Bill Venners‘ implementation of the money transfer use case in the upcoming DCI book, I was wondering how this example could be implemented in a more enterprise-y style with dependency injection. Scala has this wonderful technique of doing dependency injection called the Cake Pattern. So I tried to code up a fully working implementation of the example in Scala that talks to a MySQL database and wields a transaction manager, Atomikos in this case, where all the bits and pieces are welded together with the Cake Pattern.

For those who are not familiar with the Cake Pattern, it might be a bit confusing. However, there is an excellent post on Jonas Bonér’s blog that explains it in detail. It could be useful to read up on that first before continuing.

The use case I will try to implement in this exercise is a simple one. Imagine a user of a banking system wishes to transfer $20 from her account to that of a particular creditor. In DCI lingo, this means instantiating a context object and calling an ‘execute’ method.

object TestApp extends Application with Registry {
  val sourceAccountId = 1
  val destinationAccountId = 2
  val amount = 20.0
  val context = createTransferMoneyContext(sourceAccountId, destinationAccountId, amount)

  context.execute
}

The TestApp object has access to the createTransferMoneyContext method because it mixes in the Registry trait. This trait is responsible for actually glueing together all necessary components. The registry could be compared to a Spring application context or a Guice module. The difference is that instead of piecing together an object graph, we weld all traits together in a single object, in this case, the TestApp. More on this later. Let’s continue with the TransferMoneyContext class and how it is created.

trait TransferMoneyContextFactory {
  this: TransactionalMoneySourceProvider with MoneySinkProvider =>
  
  def createTransferMoneyContext(sourceAccountId: Long, destinationAccountId: Long, amount: Double) = 
    new TransferMoneyContext(sourceAccountId, destinationAccountId, amount)
  
  class TransferMoneyContext(val sourceAccountId: Long, val destinationAccountId: Long, val amount: Double) {   
    def execute = {
      val source = getTransactionalMoneySource(sourceAccountId)
      val destination = getMoneySink(destinationAccountId)
      source.transferMoney(amount, destination)
    }
  }
  
}

Ok, those not familiar with the Cake Pattern might scratch their heads now. I know I did when I first saw this strange nested trait business. The inner class is the DCI context class. It’s very simple, having only an execute method. The context class does nothing but bringing the actors to the stage (the money source and the money sink) and triggering the interaction. The context class receives its capabilities from the outer trait and the self-type annotations of the outer trait. In the Cake Pattern, self-types are used to define dependencies, in this case a provider for the money source and a provider of the money sink.

The MoneySource and MoneySink traits are the so-called methodless roles as per the DCI paradigm. Very simple, they only expose the methods necessary for the interaction.

trait MoneySink {
  def deposit(amount: Double)
  def updateLog(msg: String, amount: Double)
}

trait MoneySource { 
  def transferMoney(amount: Double, moneySink: MoneySink)
}

I factored out the logic for bringing instances of these traits in what I call respectively the MoneySinkProvider and the TransactionalMoneySourceProvider. Let’s take a look at the MoneySinkProvider first.

trait MoneySinkProvider {
  this: UpdatableAccountAspect =>
  
  def getMoneySink(accountId: Long) = new Account(accountId) with UpdatableAccount with MoneySink
  
}

Aha, here we first encounter the Account class. Those who have seen Jim Coplien’s presentations know that in the financial world, there is no such thing as an Account object. An account is a computation, not a record with a balance stored in a database (this is actually a good example of Event Sourcing). But here we do see an Account object, what the devil is going on?

What I did is create an illusion of an Account object. It looks like a JPA-style Account entity that has methods to manipulate the balance field, but all we have is just an abstract class.

abstract class Account(val id: Long) extends Identified {
  def deposit(amount: Double)
  def withdraw(amount: Double)
  def updateLog(msg: String, amount: Double) = println("Account " + id + " " + msg + " " + amount) 
}

In the financial world, updating the balance of an account boils down to persisting a record with the transferred amount of money. Something that looks like the following class.

class MoneyTransfer(val accountId: Long, val amount: Double) 
  extends KeyedEntity[Long] with Identified {
  val id: Long = 0
}

object Deposit {
  def apply(accountId: Long, amount: Double) = new MoneyTransfer(accountId, amount)
}

object Withdrawal {
  def apply(accountId: Long, amount: Double) = new MoneyTransfer(accountId, -amount) 
}

It has a number that represents the account number and the transferred amount (and a technical id). The two other object provide factory methods, nothing more than programming esthetics really. So what we want to do in an Account instance is persist another of those MoneyTransfer records behind the scenes of the deposit and withdraw methods. That’s where the UpdatableAccountAspect comes in. Note that the MoneySinkProvider trait has a self-type annotation specifying that it depends on the UpdatableAccountAspect.

trait UpdatableAccountAspect {
  this: MoneyTransferQueries =>
  
  trait UpdatableAccount {
    this: Account =>

    def deposit(amount: Double) = persist(Deposit(id, amount))   
    def withdraw(amount: Double) = persist(Withdrawal(id, amount))
  }
  
}

The inner trait encapsulates the capabilities that can be mixed into an Account object, giving that object the power to persist a MoneyTransfer instance in the case of a deposit or withdrawal. Summarized, when the MoneySinkProvider creates an Account instance, it mixes in the capability for updating the balance, and wraps it up under the guise of a MoneySink. As you see, there is no methodful role for a MoneySink, because there is no need for it. There is no interaction code for a MoneySink. Note that this object has no idea whatsoever of what its balance actually is! It does not have to know. It does not play a role that has to know. I’m a money sink, I receive money! Nothing more.

The MoneySource provider is a different animal.

trait TransactionalMoneySourceProvider {
  this: Transactional with UpdatableAccountAspect with CalculatedBalanceAspect =>
  
  def getTransactionalMoneySource(accountId: Long) = 
    new Account(accountId) 
      with UpdatableAccount 
      with CalculatedBalance 
      with TransactionalMoneySource 

  class InsufficientFundsException extends RuntimeException
  
  trait TransactionalMoneySource extends MoneySource {
    this: Account with HasBalance =>
    
    def transferMoney(amount: Double, recipient: MoneySink) = {
      transactionally {
        if (amount > balance) throw new InsufficientFundsException
        withdraw(amount)
        recipient.deposit(amount)
        updateLog("balance decreased with $", amount)
        recipient.updateLog("balance increased with $", amount)
      }
    }
  }
  
}

The TransactionalMoneySource is the methodful role corresponding to the MoneySource methodless role. This is where the interaction logic resides. Within the boundaries of a transaction, the MoneySource checks whether it has sufficient funds, decreases its balance, increases the balance of the recipient and updates the logs of both Accounts. Aha, so a MoneySource must be aware of its balance! How does it know that? This capability is mixed in with the CalculatedBalanceAspect.

trait CalculatedBalanceAspect {
  this: MoneyTransferQueries =>
  
  trait CalculatedBalance extends HasBalance {
    this: Account =>

    lazy val balance = calculateBalance(id) 
  }
  
}

Again the familiar nested trait structure. The CalculatedBalance trait is able to compute the current balance of the account by executing a query (in this case the computation is done in the database). Summarized, the TransactionalMoneySourceProvider creates an object that pretends it is an account, is able to calculate and update its balance and knows the necessary steps to perform a money transfer. Nice. So these are the components relevant to DCI. The other components are more of a technical nature.

Lastly, let’s revisit the Registry trait.

trait Registry
  extends TransferMoneyContextFactory
  with TransactionalMoneySourceProvider
  with MoneySinkProvider
  with UpdatableAccountAspect
  with CalculatedBalanceAspect
  with MoneyTransferQueries
  with Database
  with Transactional
  with TransactionManagerComponent
  with SessionFactory
  with DataSourceComponent {
  
  val dataSource = {
    val ds = new AtomikosNonXADataSourceBean
	
    ds.setUniqueResourceName("MyDatabase")
    ds.setUser("root")
    ds.setPassword("admin")
    ds.setUrl("jdbc:mysql://localhost:3306/TMC")
    ds.setDriverClassName("com.mysql.jdbc.Driver")
    ds.setPoolSize(1)
	
    ds
  }
  
  val userTransaction = new UserTransactionImp
  
}

This is the trait that welds everything together. It’s a concatenation of the outer traits we saw earlier. It is also the place where the actual DataSource and UserTransaction objects are instantiated. Mix in this trait in a client object and it is able to request and execute a new TransferMoneyContext instance.

That’s about it! Hope it all makes sense somehow.

The code is available on Google Code.

The dependencies:

You will need Scala 2.8.1 to compile it, SQueryl doesn’t work with Scala 2.7.x. There’s a schema generation script for creating the database. If you create the schema by hand, make sure to use the InnoDB database engine. The default one, MyIsam, does not support transactions (had me banging my head against the wall for a few hours, puzzled as to why my transactions wouldn’t work).

Feedback and suggestions are most welcome, don’t hesitate to drop me a line!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s