vlambda博客
学习文章列表

企业应用架构模式之事务脚本(Transaction Script)

不闻不若闻之,
闻之不若见之,
见之不若知之,
知之不若行之。
——荀子
Martin Flower《企业应用架构模式》堪称经典,重读后有一种醍醐灌顶的感觉,殊不知我们研发一直使用的架构模式,读完后才发现原来就是它(Transaction Script),一直使用着Java面向对象语言,编写系统的时候则面向过程开发。

01

事务脚本(Transaction scripts)使用得最多的方式,其特点是: 简单实用,易于理解。比如,企业项目大多数都是采用事务脚本方式进行研发,后面我们来看案例,则清晰明了。
那什么是事务脚本呢?
使用过程来组织业务逻辑,每个过程处理来自表现层的单个请求。事务脚本方式是将这些逻辑组织成单个过程,在过程中直接调用数据库,或者通过一个简单的数据库封装器。每个事务都有自己的事务脚本,尽管事务间的公共子任务可以被分解成多个子程序。
说简单点就是实现一个功能,就直接写一个方法。比如,我们的系统需要加一个功能,首先先找到那个业务类,然后再定义一个方法加上一堆参数。然后就开始写这个方法的实现代码,要是逻辑复杂点,这个方法里会有一堆的 if esle 判断或者与别的业务板块之间接口相互调用。如果逻辑不复杂,这种实现到没什么问题,也很方便。
对于事务脚本来说,很多时候都有3个具体的类来构成,他们一般分别是Sevice,Dao,JavaBean,Service类主要用于组织业务逻辑,Dao类主要用于实现数据的存储,有些甚至不需要Dao层,直接使用数据库封装器,  JavaBean主要用于数据的封装。
事务脚本方式中的JavaBean,被称为贫血模型,对象中除了 set 和 get 方法外几乎就没有其它的方法,整个对象充当的就是一个数据容器,所有的业务方法都在一个无状态的Service类中实现,Service类仅仅包含一些行为。

案例图中实现行为全在 OrderServiceImpl 类中,这也就是使用事务脚本时,脚本通常位于服务类,也就是 OrderServiceImpl 类。数据对象 Order 是纯数据容器,没有任何行为。

下面我们来看一个案例,该案例是将所有关于 Order 相关的操作都在 OrderServiceImpl 中实现。

控制器 OrderController
 
   
   
 

  1. public class OrderController {

  2. // save

  3. public String save(){

  4. String json = “”;

  5. return json;

  6. }

  7. // settlement

  8. public String settlement(){

  9. String json = “”;

  10. return json;

  11. }

  12. // xxx();

  13. // xxxx();

  14. // xxxxx();

  15. }




接口 OrderService


 
   
   
 

  1. public interface OrderService {

  2. // save

  3. public void save(Order order);

  4. // settlement

  5. public void settlement(Order order);

  6. // getOrders

  7. public List getOrders();

  8. // xxx();

  9. // xxxx();

  10. // xxxxx();

  11. }


实现 OrderServiceImpl

 
   
   
 

  1. public class OrderServiceImpl implements OrderService{

  2. @Override

  3. public void save(Order order) {

  4. // TODO Auto-generated method stub

  5. // 实现保存相关的业务

  6. }

  7. @Override

  8. public void settlement(Order order) {

  9. // TODO Auto-generated method stub

  10. // 实现结算相关的业务

  11. }

  12. @Override

  13. public ListgetOrders() {

  14. // TODO Auto-generated method stub

  15. // 实现获取订单列表的业务

  16. return new ArrayList();

  17. }

  18. // xxx();

  19. // xxxx();

  20. // xxxxx();

  21. }


类 Order 


 
   
   
 

  1. public class Order implements java.io.Serializable{

  2. private static final long serialVersionUID = 1L;

  3. private int no;

  4. private double money;

  5. private int id;

  6. public int getNo() {

  7. return no;

  8. }

  9. public void setNo(int no) {

  10. this.no = no;

  11. }

  12. public double getMoney() {

  13. return money;

  14. }

  15. public void setMoney(double money) {

  16. this.money = money;

  17. }

  18. public int getId() {

  19. return id;

  20. }

  21. public void setId(int id) {

  22. this.id = id;

  23. }

  24. public Order(int no, double money, int id) {

  25. super();

  26. this.no = no;

  27. this.money = money;

  28. this.id = id;

  29. }

  30. public Order() {

  31. super();

  32. }

  33. }


通过案例可以看出每次要添加一个方法肯定是找到接口 OrderService 然后紧接着在实现类 OrderServiceImpl 进行实现,如果涉及到与前端进行交互还需在 OrderController 中进行添加方法。这就是事务脚本的缺点,代码的组织性差,可读性差和冗余多。
其实,事务脚本还存在另外一种方法来组织事务脚本类。上面案例中我们是将所有实现方法都在类 OrderServiceImpl 进行实现。那另外一个方法则是每添加一个功能或者说每加一个事务脚本则需要添加一个类,也就是说一个事务脚本对应一个类。(会有OrderServiceImplxxxx、OrderServiceImplxxxx2 等等类,名字会根据功能名称进行命名,这不在赘述。)


02


事务脚本(Transaction Script)是不是很熟悉,很简单。没错,事务脚本胜在简单。对于只有少量逻辑的应用程序来说,使用这一模式非常自然,无论在性能上还是理解上都不会带来太大的开销。


但是,当业务逻辑越来越复杂时,使用这一模式就会越来越难以保持良好的设计。它特有的问题是事务之间的冗余代码。比如,我们系统中有挂单结算与直接结算功能,通过两个接口实现该业务会发现两个方法的内部会存在很多重复性代码。


因此,使用事务脚本方式的时候会发现需要频繁的提取子程序,也就是公共方法,但在实际提取过程中仍会出现很多重复性代码,随着系统功能的增多,逻辑变得复杂,代码的维护成本则会逐渐提升。所以,事务脚本的方法在针对简单逻辑的系统是可以提升开发速度。但是,对于复杂的业务领域则需要领域模型(微服务构建系统的方式,下一篇来讲该方式)。


03


以上就是事务脚本方式,读完后自己在想一个问题,在编程系统的时候能不能把每个子系统、子模块采用该方式来进行编程。个人的想法是假如各个子系统、子模块的业务不复杂,且之间的联系较少或者说没有,可以这样操作。(个人说的也不全对,因为事务脚本的方式与领域模型两者很难给出分界点。)相反,则不建议这样操作,虽然研发速度会快。但是,仍会存在子系统、子模块不易于维护,不易于理解等等问题,后期涉及到各个子系统间通信则会更加繁琐。


那能不能先使用事务脚本后,后面再升级成领域模型呢?答案肯定是不行的,因为从面向过程到面向对象转,这个过程就很难,而且还得保证系统的可行性。因此,早期选择正确的决策使用哪种模式构建系统是最好的方法。


那听完这么多是不是在构建系统的时候都不使用事务脚本了,明明用的Java面向对象语言,还得用面向过程的方式开发,逼着自己非得要使用面向对象的方式来开发。


有一句话是“杀鸡焉用牛刀”,我们不要盲目排斥事务脚本,应具体问题具体分析,如果是一个简单的问题,事务脚本这一简单方法不仅可以帮助你提升研发速度,还可以是系统运行的很好,何乐而不为呢。相反,业务领域复杂则需考虑领域模型的方式。



-END-


一切即心,心即一切