Flicker1985's Blog

Everything should be made as simple as possible, but not simpler.
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

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来实现这个例子,我想应该是这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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来实现这个例子又是如何呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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   Fei He  阅读(1845)  评论(9编辑  收藏  举报

编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
点击右上角即可分享
微信分享提示