业务逻辑层之事务脚本与领域模型

esbaoly 2014-07-11

在前面的博客中,已了解了前端控制器,页面控制器,应用控制器这三种表现层模式,如果说它们精心安排了外部世界与系统内部的通信,那么业务逻辑层的工作则是处理应用程序的业务部分。业务逻辑层应当远离那些外部的“噪音”。业务逻辑是整个应用程序的根本目的所在,系统的其它部分都是为这部分服务的。

这里介绍两种经常使用的领域逻辑模式:事务脚本模式和领域模型模式。

一、事务脚本

1.1 概念

Transaction Script:使用过程来组织业务逻辑,每个过程处理来自表现层的单个请求。貌似有点太抽象了。大多数业务应用都可以被看作是一系列事务,有时候,事务可能就显示下数据库的信息,有时候,也可能涉及许多校验和计算的步骤。事务脚本则将所有这些逻辑组织成单个过程,而且每个事务都有自己的事务脚本,就是都有自己的执行过程,但注意的是事务间的公共子任务可以被分解成多个子程序。

1.2 为什么要使用事务脚本

事务脚本模式的好处在于你可以很快就得到想要的结果。每个脚本都能很好地处理输入的数据并操作数据库来保证得到想要的结果。因此它是一个快速而有效的机制,且不需要投入大量时间和精力在复杂的设计上,对于小型且工期较紧的项目再适合不过。

1.3 实现事务脚本

具我工作经验观察,许多程序员都浑然不知地在使用这种模式,包括本人以前。

现在假设有发表博客和删除博客的业务,那将这两个业务分别看成两个事务脚本。

<?php
//这里创建一个基类,作数据处理,假设用的是pdo
abstract class Base {
    function __construct() {
        //这里用了到之前博客讲的注册表
        $dsn = \woo\base\ApplicationRegistry::getDSN( );
        if ( is_null( $dsn ) ) {
            throw new \woo\base\AppException( "No DSN" );
        }

        self::$DB = new \PDO( $dsn );
        self::$DB->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    } 

    protected function doStatement() {
        //执行sql
    }
}

class blogManager extends Base {
    static $add_blog =  "INSERT INTO blog ( name ) values( ? )";
    static $del_blog = "DELETE FROM blog WHERE (?)"; 

    //添加博客事务脚本
    function addBlog(...) {
        //处理参数,拼写add_blog格式的sql,调用父类doStatement执行,通知好友,一系列的子程序。。。
    }
    
    //删除博客事务脚本
    function delBlog(...) {
        //处理参数,拼写del_blog格式的sql,调用父类doStatement执行。
    } 
}
?>

 这个例子十分简单,但正因为它的简单,才正好体现了事务脚本的优势之处。如果写一个较为复杂应用程序,这种方式使项目不太容易扩展,因为事务脚本总是不可避免地互相渗入,从而导致代码重复。

二、领域模型

2.1 概念

Domain Model:很难用语言说清楚。简单的说就是领域模型象征着真实世界里项目中的各个参与者。“万物皆对象”的原则在此体现得淋漓尽致。在其他地方对象总是带着种种具体的责任,而在领域模式中,它们常常描述为一组属性及附加的代理。它们是做某些事的某些东西。

2.2 为什么要使用领域模型

现实代码中,会有很多事务脚本模式的身影,会发现重复代码是个普遍问题。当不同的事务要执行相同的任务时,重复貌似是最快的解决办法,但这大大增加了代码维护的成本。有时也可以通过重构来解决,但慢慢地复制粘贴可能成了开发中难以避免的一部分。

2.3 实现领域模型

为实现对比,引用事务模型的例子,并将领域模型类直接映射到关系数据库的表(这样做会使得开发变得简单)

<?php
//这里创建一个基类,作数据处理,假设用的是pdo
abstract class Base {
    function __construct() {
        //这里用了到之前博客讲的注册表
        $dsn = \woo\base\ApplicationRegistry::getDSN( );
        if ( is_null( $dsn ) ) {
            throw new \woo\base\AppException( "No DSN" );
        }

        self::$DB = new \PDO( $dsn );
        self::$DB->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    } 

    protected function doStatement() {
        //执行sql
    }
}

class blogModel extends Base{
    function addBlog(...){}
}

//创建一个领域模型的基类
abstract class DomainObject {
    private $id;
    //$id为表数据的主键id
    function __construct( $id=null ) {
        $this->id = $id;
    }

    function getId( ) {
        return $this->id;
    }

    //牢记 万物皆对象
    static function getCollection( $type ) {
        //获取要操作的对象    
    }
}
class Blog extends DomainObject {
    private $name;
    private $feed;

    function __construct( $id=null, $name=null ) {
        $this->name = $name;
        $this->feed = self::getCollection("\\woo\\domain\\Feed");
        parent::__construct( $id );
    }

    function addBlog(...){
        //调用blogModel的方法添加
        //再调用feed发送通知给好友
    }

    function setFeed( FeedCollection $Feed ) {
        $this->feed = $Feed;
    }
}
?>

2.4 小结

领域模型设计得简单还是复杂取决于业务逻辑的复杂度。使用领域模型的好处是:当你设计模型时可以专注于系统要解决的问题,而其他的问题(如持久化和表现等)可以由其他层来解决。

在实现项目中,大多数程序员在设计领域模型时还是把一半的注意力都放在数据库上。将领域模型和数据层分离会导致一定的代价,你也可能会将数据库代码直接放入模型中(尽管你可能会使用一个数据入口来处理实际的SQL)。对于相对简单的模型,特别当类与数据表一一对应时,这样方法是完全可行的,可以减少因协调对象和数据库而创建外部系统导致的时候消耗。

相关推荐