谈到应用程序的分层架构,很多人首先想到的是一个标准的模型,包括控制器(Controller)、服务层(Service)和数据访问层(Mapper)三个主要部分。这听起来似乎很直观和简单,但实际上,很多开发者在实施时并没有明确区分这些层次的具体职责。例如,一些项目中,控制器层的代码量反而超过了服务层,而服务层仅仅作为一个传输介质,这反映了开发过程中容易被忽视的问题。这种模糊的层级职责划分,最终可能导致架构的混乱,使得代码难以复用和维护
如何进行分层
一个好的应用分层需要具备以下几点:
方便后续代码进行维护扩展;
分层的效果需要让整个团队都接受;
各层的职责边界清晰。
图片
阿里巴巴的编码规范细化了应用程序架构的多个层次,旨在更清楚地界定各层的职责和作用。这些层次包括:
开放接口层:这一层负责将服务层的功能通过RPC接口或HTTP接口向外暴露,同时负责网关的安全和流量控制。
终端显示层:负责在各种客户端上渲染和显示信息,使用不同的技术如Velocity、JavaScript、JSP进行页面渲染和移动端展示。
Web层:处理访问控制和转发,进行基本的参数校验和一些不需要复用的简单业务处理。
服务层(Service层):执行更具体的业务逻辑处理。
管理层(Manager层):作为一个通用业务处理层,具备三个主要功能:封装对第三方平台的调用、下沉Service层的通用能力(如缓存和中间件处理)、以及与数据访问层(DAO层)的交互,实现对数据访问对象的复合使用。
数据访问层(DAO层):直接与数据库(如MySQL、Oracle、Hbase)进行交互的层级。
优化分层
首先需要说明的是,如果 RPC 框架选用 Thrift,可能会比其他的 RPC 框架多出一层,作用和 Controller 层类似:
图片
阿里巴巴的架构分层规范中,最顶层由 Controller 和 TService 构成,主要职责是处理轻量级的业务逻辑、进行参数验证和异常管理。
这一层设计的目的是保持足够的灵活性,以便在需要时可以方便地更改或替换接口类型,因此这里的业务逻辑应尽可能简化,有时甚至可以避免实现具体逻辑。
紧接着的是 Service 层,承担着具体的业务逻辑处理。在这个层级,建议采取一种方法:让每一个 Controller 操作都对应一个 Service 方法。
这样做的好处是避免将业务逻辑的编排混入 Controller 层,从而在将来需要接入其他接口类型,如 Thrift 时,可以避免重复编排业务逻辑,减少代码的重复和维护成本。这种方法强调了在不同层之间保持清晰的职责分离,以提高代码的可维护性和可扩展性。
图片
这样大量的重复工作必定会导致开发效率下降,所以你要把业务编排逻辑都放进 Service 层中。
图片
接下来是 Manager 层,这一层充当了可复用逻辑的核心角色。在这个层面上,Manager 可以是负责单一功能的服务,如缓存(Cache)、消息队列(MQ)等;同时,它也能够处理更复杂的任务,比如当需要同时调用多个 Manager 服务时,可以将它们组合成一个综合性的 Manager,以处理更为复杂的业务逻辑,如在逻辑上进行类似于数据库连表查询的操作。
再来看 DAO 层,这是数据库访问层。主要负责“操作数据库的某张表,映射到某个 Java 对象”,DAO 应该只允许自己的 Service 访问。
在阿里巴巴的编码规范中,分层领域模型的转换是一个关键的设计考虑,以确保数据在不同层之间传递时的清晰性和准确性。以下是一些核心领域模型及其用途的概述:
DO(Data Object):数据对象,直接与数据库表结构对应,通过 DAO(数据访问对象)层传输数据源对象。这确保了数据层与数据库的直接映射,便于操作数据库。
DTO(Data Transfer Object):数据传输对象,用于服务层或管理层向外部传输的对象。DTO 主要用于跨层通讯,封装了需要传输的数据,有助于减少一个方法调用所需要传递的参数数量,简化远程接口调用。
BO(Business Object):业务对象,由服务层输出,封装了业务逻辑的对象。BO 体现了业务模型的概念,通常用于封装具体的业务逻辑和业务状态,反映了业务操作的结果。
AO(Application Object):应用对象,位于 Web 层与服务层之间,是一个抽象的复用对象模型,非常贴近于展示层但复用度不高。AO 主要用于处理特定于应用的逻辑和状态,作为不同服务层之间数据传输的中间层。
VO(View Object):视图对象,通常由 Web 层传输至模板渲染引擎层的对象。VO 主要用于展示层数据的封装,专门为用户界面定制,包含了用户界面展示所需的数据。
Query:数据查询对象,用于在各层之间传递查询请求。它允许将查询条件封装为一个对象,使得方法调用更加清晰,同时避免了使用诸如 Map 这类无结构的数据类型来传递多个查询条件,提高了代码的可读性和可维护性。
图片
每一个层基本都有自己对应的领域模型,而有些人过于追求每一层都用自己的领域模型,这就导致在一次请求中,出现多次对象转换。
一个折中的方案是:
允许 Service/Manager 可以操作数据领域模型。
Controller/TService 层的领域模型不允许传入 DAO 层,这样就不符合职责划分了。
同理,不允许 DAO 层的数据传入到 Controller/TService。