Flicker1985's Blog

Everything should be made as simple as possible, but not simpler.
Transaction Scripts vs Domain Model

  最近和一些朋友在闲聊之中发现不少人对于Transaction Scripts的认同和对于Domain Model的不理解都让我非常的诧异。所以就有了这篇文章。

  首先,什么是Transaction Scripts,什么又是Domain Model呢?据我所知这两个概念都是Martin Flower在《Patterns of Enterprise Application Architecture》中提出来的,指的是两种对于Domain Logic的组织方式。具体的定义如下

  Transaction Scripts是指:

     “Organizes business logic by produces where each procedure handles a single request from the presentation.”

  Domain Model是指:

   “An object model of the domain that incorporates both behavior and data.”

  对于我们programmer来说,也许代码更直观,那么我们就看看这两种pattern用代码如何表达,这里我有一个简单但是又老的掉牙的例子,银行转账。(当然实际中银行转账涉及的东西非常的多,比如security,transaction,log等等。但是转账这个业务的核心就是两个账户金额的变动,而例子毕竟只是为了说明问题。)

    如果我们用Transaction Scripts来实现这个例子,我想应该是这个样子:

class Account
  attr_reader :id 
  attr_accessor :balance
  
  def initialize(id, balance)
    @id, @balance = id, balance
  end
end

class TransferService
  def initialize(account_repository)
    @account_respository = account_respository
  end
  def transfer(from, to, amount)
    from_account = @account_respository.find(from)
    to_account = @account_respository.find(to)

    raise "Insufficient fund" if amount > to_account.balance
    to_account = to_account.balance - amount
    from_account = from_account.balance + amount
    
    @account_respository.save from_account
    @account_respository.save to_account 
  end
end

那么我们如果采用Domain Model来实现这个例子又是如何呢?

class Account
  attr_reader :id, :balance
  
  def initialize(id, balance)
    @id, @balance = id, balance
  end
  def withdraw(amount)
    raise "Insufficient fund" if amount > @balance
    @balance = @balance - amount
  end
  
  def deposit
    @balance = @balance + amount
  end
end

class TransferService
  def initialize(account_repository)
    @account_respository = account_respository
  end
  def transfer(from, to, amount)
    from_account = @account_respository.find(from)
    to_account = @account_respository.find(to)
    
    from_account.withdraw amount
    to_account.deposit amount
    
    @account_respository.save from_account
    @account_respository.save to_account 
  end
end  

第一眼看到这两段代码,你肯定会说“这不就是把Account中的Balance处理代码不一样吗?”。你说对了,这两个pattern的区别就在这里,在Transaction Scripts中,Account中Balance的维护是在TransferService中完成;而Domain Model中,Account这个model自己维护自己的Balance。

  那么对于这样一个银行转账的例子,究竟business是什么呢?

  回答这个问题之前,首先,我们必须明白service和business的区别和关系。对于一个应用程序系统来说,service是系统对外的接口。通过这些service,你可以了解这个这系统的功能,而business则是系统内部的规则。service对于business来说应该是透明的,也就是说business本身不会意识到service的存在。就我们这个银行转账的例子来说,我们通过TransferService了解到银行有转账这个service接口,但是这个TransferService执行中则需要遵循一个规则,就是账户转出的钱必须等于不等大于账户余额。我认为这个规则才是这个例子的business。

  了解到了service和business之间的区别,我们首先职责分离的角度来看这两个pattern。Transaction Scripts讲Service与business绑在了一起,这样首先会使得business分散在各个不同的service之中,业务在代码之中变得不清晰,其次,service会随着business的复杂程度的递增而变的越来越厚。Domain Model将business和service的职责的分离,使得业务变得清晰。紧接着给我们带来的就是隔离变化。比如说我如果某一天的我的business发生了一些变化,这时我们对service的影响就相对较小,反之如果business分散在所有的service之中,business的变化对于service的影响可想而知。

  最后想说点体外的话。为什么相对于Domain Model来说Transaction Scripts更容易被人理解?我觉得这是因为我们大脑的思维方式是基于过程的思维方式,面向对象只是后天的训练和学习的结果。所以我想说,如果你发现你java或者ruby代码,和你写C的代码感觉没有区别,那么只能说明你在用面向过程的方式写java代码。

posted on 2011-03-28 23:33  Fei He  阅读(1824)  评论(9编辑  收藏  举报