应用分层模型研究
2018年06月13日


常见应用框架 分层模型说明

调用层次:(控制层-可选)——>业务逻辑层——>数据处理层


1、控制层:非必须,可选,一般由框架层面统一处理。
    此层由一系列拦截器(可以是前置、后置、环绕等类型的拦截器)组成,用于拦截请求,对请求进行统一的解析和处理,然后决定是否继续执行,决定后续应该调用哪些业务逻辑对象的业务方法来处理请求,并将业务层的处理结果,进行统一加工,再返回给请求发起方。


2、业务逻辑层:真正处理业务请求,调动各种资源(mq、redis、mysql等)和子处理逻辑

    当然,业务逻辑的粒度有粗有细,所以这一层又可以进一步划分。划分方式也多种多样,具体举两例:

1)分为manage——>service两层,service分为很多技术种类,每种Service只处理一个技术种类的数据,比如操作redis,mysql,mq等。而manage是调用各种service(RedisService, MySqlService...),来封装一个更粗粒度的业务逻辑。按 数据的技术种类 来划分Service的好处在于,从技术上可以对Service进行一些统一的封装和处理,劣势是Service是从技术角度区分的,通常它不是一个完整的业务逻辑,这样业务被打散,不便于从业务视角进行开发。

2)同样分为manage——>service两层,但是service的定义不一样,Service定义为细粒度的业务逻辑,它的所有接口方法都是一个完整可用的业务逻辑,可以直接对外提供服务,而manage和service类似,只是manage封装的是更粗粒度、更复杂的业务流程。好处是,按业务逻辑视角进行开发和测试,目标清晰,劣势是service里面可能混杂了对多种数据的交叉业务处理(比如有redis的数据处理、mq的数据处理,还有mysql的数据处理),不便于统一升级、重构和排查问题。


3、数据处理层:该层就是为了解决业务逻辑层的数据处理问题

    如果没有数据处理层,就会像上面举例提到的那样,可能要在业务逻辑里面,直接处理多种数据。数据处理层,就是专门来处理某一个技术类型的数据,它是按数据的技术类型区分的,比如针对MySQL、mq、redis。

    值得说明的是,数据处理层,针对某个技术类型,它又有可能有多层,但是他们是一个整体,只要都是在处理同一个类型的数据,就应该视为同一层。比如数据库层,通常内部又有mybatis封装层——>mybatis层——>jdbc驱动层,也有一些公司把数据处理层分为:数据管理层(data managing layer,负责管理多个数据)——>数据处理层(data processing layer,负责组装和处理数据)——>数据访问层或者持久层(data access layer,data persistence layer)或者ORM层(Object Relational Mapping)(屏蔽数据驱动层的原始数据格式,负责将数据进行包装成自定义的Java POJO对象)——>数据通信层或者数据驱动层(data communicating layer,负责处理数据协议,和远程数据源交互)。

    该层用到Mybatis技术、通用Mapper技术和Spring相关技术(https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#orm)。


注意:参数校验应该放在哪一层?

    从根本上讲,方法的参数校验,与分层无关,究竟是否要做参数校验,取决于方法的使用场景:方法的调用方是谁?每个参数是否需要校验,校验的规则是什么?

    按照上面的分层定义,控制层更趋向于统一的管理,它跟具体的业务关系不大,所以它不应该参与到具体的业务参数的校验中,但是在控制层中,可以做一些通用的、统一的校验。

    业务逻辑层,显然,应该要对参数进行校验的,参数合不合格,直接影响到后续的执行,所以,首当其冲,就应该对参数进行业务上的校验。业务层,根据业务规则,可以在每个服务的入口处 进行参数校验。

     数据处理层,同样,这一层是相对较独立和公用的层,它和具体的业务逻辑是间接的关系,所以,在这一层,为了提高效率,可以信任调用方已经做过参数校验,在这里就不用再做参数校验了,即,“将参数校验前置”,这样还有一个好处是,参数不会重复校验。但是,如果将参数校验前置,那么每个调用方都需要写一遍参数校验的代码,会增加一些复杂性。所以,在复杂性和性能之间权衡,如果参数校验十分必要,而且规则特殊,而且本身执行速度快故不需要前置校验,那么还是应该在当前方法做校验——典型的例子就是 工具类,在工具类里面通常会对参数进行校验。但是对于数据处理层的方法,通常都是有较大的性能开销的,还是建议将参数校验前置,当然,不能一概而论,一切要在理解原理的基础上做决定。


下面说明,我开发的Fast框架,它的每一层具体内容:


控制层:

    fast-mini版无控制层;

    fast-web版控制层由Fast框架基于Spring封装,提供了参数、异常和返回值的自动处理,另外,它有一个“web层”controller,仅仅是对参数进行检查和加工;

    fast-dubbo版控制层由Fast框架基于dubbo封装,提供了参数、异常和返回值的自动处理。

 (在这一层,开发完全不需要写代码)


Web层:

    这是一个特殊的层,偏向于控制层,但是也有一些和Web相关的业务逻辑。在Web层,只做跟UI交互和Web API相关的操作。

    理论上讲,1)它是在UI层的基础上对出参、入参进一步加工(其实也可以放到UI层,在JS里面做这些工作,只是麻烦一些);

    2)它是把一部分和Web相关的业务操作独立出来,比如操作Session、HttpServletRequest、HttpServletResponse等;

    3)对于Spring等MVC框架,Web层还充当了配置请求和Action方法的映射路由关系。


业务逻辑层:

    fast-demo版,由于功能简单,都没有默认的业务逻辑层,开发者可以根据自己需要自由编写,根据上面的分层模型定义,业务逻辑层可以有一层(Service层)或者两层(Manage层+Service层),如果是对远程提供服务(RPC),则必须编写接口(Interface),如果只是单体应用的内部服务,可以不编写接口,但是建议都编写接口,以便以后对外提供服务。

 (在这一层,所有代码,都需要开发自己编写)


数据持久层(data persistence layer,包名com..dao.*Mapper):

    该层由一系列负责操纵数据表实体(*Entity)的*Mapper类组成,这些类负责把数据进行持久化,一般是把数据保存到数据库中。

 (在这一层,自动生成的代码,能满足日常80%的功能,开发基本上不需要写代码)


数据管理层(data managing layer,包名com..dms.*Dms):

    该层由一系列的*Dms(Data Manage Service)类组成,调用一个或多个数据持久层服务,以实现对持久层的数据进行组合和加工,并保证事务一致性。

    之所以要专门设计一个数据管理层,是因为我们用的Mybatis,而且架构上要求Mapper职责要简单,尽量把数据的处理逻辑放到Java程序中处理,而不是交给数据库,下面摘录了 达达科技《高性能服务端优化之路》 文里的一段话:“经验教训....使我们制定了SQL最佳实践,其中一条便是程序中禁用或少用join,而应该在程序中组装数据,让SQL更简单”。我们使用的Mybatis,在Mapper中不适合组装数据,所以单独设计一个数据管理层来干这个事情,顺便处理数据库事务。

 (在这一层,自动生成的代码,能满足日常70%的场景,开发只需要编写少量代码)