浅谈工程目录结构——包命名和分层职责问题

首先,基于约定俗成的命名习惯,我们常见的包名大致可罗列如下:

数据对象类:bean/entity/pojo/model/dataobject/domain/enum/constant;

持久层仓库资源类:dao/repository/mapper;

业务层:service/action/manager/biz

控制器层:controller/web/ctrl/facade

工具设施层:utils/tools/

切面横切逻辑:aspect/aop

拦截器:interceptor/filter

全局配置:config/configration

自定义异常:exception

转换器:converter

表单对象:form      

......
以上单词及相应的复数形式,我们较为常见,很多基于类的职能进行命名,也有根据采用不同框架历史沿革进行命名,如mabatis用mapper,SSH框架的业务层用action,springJPA的持久层用repository等;命名虽不强制,但保持这种事实上的约定,交流项目、维护管理会很方便。

尽管大家在项目着手时都会有模有样的攒出这样一个架子来,但包括我在内的很多新手对于各层级、各包的职责可能依然会混淆。在经过思考和比对一些我所见过的项目,本文做一些个人理解阐述,如有不当,欢迎指正。

首先,我提倡包结构不要嵌套过深,在src/main/java/com.groupName.projectName目录下,按照上述分层分好包后,每个包最好不要超过两层子包的嵌套。比如service下,可能有接口和Impl实现包,实现包下又可能又分出一个xxxmock包用于打桩测试,这就可以了,不要再深入嵌套了。

然后说一说各层的职责。

控制层:通常作为业务驱动的事件入口,一般与客户端(包括桌面、web等GUI、命令行等)打交道,作为interface(界面/接口),是客户端与服务层之间建立的稳定的抽象屏障;其核心职责就是负责接收请求,并校验参数,数据绑定、适配转换,调用服务层组件后,处理结果并响应。很多人伊始,不自觉地就会把业务逻辑写到控制层,这不能说不对或不行,而是违背了分层的初衷,混淆了层次。提起适配,也有很多人喜欢将vo、bo、po、dto等对象分清楚,这本是为解耦而为,但也有朋友混合在各层传输它们。比如把control层用的Vo传给Biz层,我认为在biz层去convert,这从源码依赖的角度来说,就违背了单向依赖、向下依赖的原则。依我之见,应该“谁主张,谁举证”,即谁是调用方,谁负责convert工作。也就是说应该在control层拿到vo接收的参数后,在调用service层方法时,提前转换成service层需要的bo(这里假设bo只是service层的数据对象,因为也有对面向对象高度推崇者认为BO是封装了数据和方法的业务对象);service层声明接口时,就强硬的对外声明“我需要xxxBo”;同理,Dao接口就对外声明:“我需要xxxPo”对象;提取数据时也一样,service层调用Dao层返回xxxPo时,自己转换成业务对象。反之,如果被调用方负责转换数据,那就需要知晓上层数据结构,违背了“上层对于下层而言不可见“的原则,况且这也不是它分内的事,服务组件就要专注业务逻辑。

业务层:我这样理解业务层,它通过组织例程单元及各种资源,进行脚本化形成的应用服务上下文。本质上,是分布在不同命名空间里的资源在此按一定时序排列组合形成的上下文场景。然后对外暴露服务接口提供内核服务。虽然开发过程中会考虑到与控制层的接洽,但考虑到模块复用,理论上该层可不受限于上层约束,独立的制定接口。整个过程就像一个boss抓壮丁去组织各个worker干活一样,一个派发函数,或一个上下文场景类,乃至一整套服务模块,都在扮演一个调度员的角色。

持久层:作为数据访问对象,与持久化机构进行读写交互,不关心业务逻辑,只执行上级命令如实提供数据源,数据怎么用不是它该关心的。当然,持久层是个广义的含义,我认为能提供数据仓储作用的对象都可以置入该层,比如缓存组件。

模型层:严格的说,它不算是某一层,层是一个基于业务流所处不同阶段而划分的概念;模型中,无论是视图对象、普通值对象、或带有oid的实体数据对象,都可以说是被操纵的数据。因为编程无外乎就是对象调方法或方法操纵对象,用自然语言表述就是主语-动词-宾语的组合过程。当然你可以说方法也可以作为数据传入方法或返回,的确,往深了说,一切都是对象/数据,方法/函数也是对象;一切又都是函数,因为对象的定义也需要通过构造、选择、赋值函数来进行定义;一切对象在一个命名空间/上下文下,又可作为属性...所以,过程和数据抽象在本质上是统一的。我们这里考虑一般意义上的编程范式,将数据和过程分离,那么模型这一层的确是贯穿整个应用各层始终的。不难看出,所谓的vo\bo\po其实就是模型在业务流转过程中不同阶段的特称。

外围设施:比如util包,我个人习惯把无关核心业务逻辑的其他模块称为外围设施。用来提供基础、辅助职能,如字符串、日期处理、rpc通信、编解码等工具类,通用小部件等。就像一个国家的基建事业一样。

环境:比如config包,一般用于工程整体环境、框架配置等。

其他按具体职能命名的包就不用赘述了。

总之,既然按照MVC去分层架构,就是为了降低耦合、维护起来清晰、容易复用,因此一定要认真审视各层的角色和职责,以及所开发组件的业务边界;另外在分配源码文件存放的位置时,需要操作的私有数据就放在私有空间,共同依赖使用的数据就放在公共空间,这种关系小到类得成员变量和方法的局部变量,大到分包时包内私有类和包外共同依赖的类、甚至分工程时对包的依赖……道理都是相通的,“其大无外,其小无内”,分形递归,妙不可言。说来也简单,就是“让上帝得归上帝,凯撒得归凯撒”——要得位。该是哪得就是哪的。顺其自然得编程,让事物的意志在其冲动下自然的外显,就像上帝抓住你的手让你那样写一样。怎么样,是不是有点哲学意味了?不要让工程结构徒有其形,内部依赖混乱一塌糊涂。那就金玉其外败絮其中了。

你可能感兴趣的:(设计模式)