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代码。