增删改查是业务基本功能,目前大家都是按照一种固定的模式开发,但这种开发模式我认为有些地方不是很合理。
三层架构
三层架构,即表现层、业务逻辑层和数据访问层。假设有Example实体,现在我们要实现该实体的增删改查,按照目前的开发模式,UML类图是这样的:
看到类图,大家第一感觉是什么?类太多了!我们只是想要做个简单的增删改查,却要创建15个类!(其中表现层2个,业务逻辑层7个,数据访问层6个,黄色的属于公共类不计)。
15个类什么概念?公有云合约有8大类合同,那么就得创建120个类,再加上合同对应的8大类结算,就是240个类了,简直是类爆炸。那么要怎么精简呢?
- 移除所有的扩展类(extend结尾)
目前约定了所有相对于扩展类的非扩展类都是固定的且不应修改,有新功能就在添加到extend类上。但是首先非扩展类里的操作对于业务来说可能不是必需甚至是不应该提供的,其次非扩展类里的操作可能并不能满足实际业务需求(实际还是会修改),最后这种约定也是脆弱的,开发者想修改还是能轻易修改。 - 2个service(接口加实现)
目前service的写和查做了分离,另外还有专门的审批service,但是从业务对象上划分它们都是相同的,可以合并成1个。 - 2个dao(接口加实现)
无论是JdbcTemplate,还是EntityManger,又或者是ElasticSearchTemplate、RedisTemplate等等,它们都没有按操作类型(保存|修改|删除)的维度再去划分类,所有数据访问层操作放1个类里面就可以了。
以下是我认为比较合理的三层架构:
如上,忽略黄色公共类不计,现在再做增删改查只需创建6个类了(因为使用了Spring-Data-Jpa,所以有3个dao类)。
代码生成
上面提到,目前做一个简单的增删改查要创建15个类,但是却没有人反映过繁琐,原因就是使用了模板引擎来生成代码。代码生成的好处是不需要手写代码,那么它的缺点呢?
比如我要在所有activity里面添加新的公共规则,那么得去所有已生成的activity里面一个个添加;比如我要修改所有service中分页查询的内部实现,那么也得去所有已生成的service里面一个个修改。有人会说直接再重新生成不就可以了吗?可是万一我们已经对生成的文件做了部分修改呢?
可见,代码生成是一种伪封装,看似提供了便利,却不利于扩展和修改。在Spring Boot官网,明确指出Spring Boot的一项特性就是“Absolutely no code generation”,这是有原因的。
代码生成并非是完全不好,主要是看怎么使用,上面的场景我们就应该考虑封装而不是代码生成,Don’t Repeat Yourself !
持久层封装 VS (Spring-Data-Jpa & JdbcTemplate)
可能是出于性能原因考虑,目前持久层没用到Spring-Data-Jpa/Hibernate,而是对JdbcTemplate做了层封装,对此我有几点疑问:
- 是否遇到过因为使用Jpa导致性能很差且不能优化的场景?
- 是否有必要为了一点性能的提升(可能某些场景下Jpa性能更高)而放弃高效率开发?
- 既然使用JdbcTemplate,为什么又按Hibernate操作实体的方式来对其做封装?
- 既然未使用Jpa,为什么还要使用Jpa注解?为什么不自定义注解?
- 何不考虑使用Mybatis?
在改造后的框架中,我并没有再去封装持久化操作,而是直接使用Spring-Data-Jpa & JdbcTemplate。使用Jpa的原因是现阶段主要是快速完成业务开发,而我们的业务对象之间的关系可能是复杂的,在复杂对象的级联操作上Jpa有优势。使用JdbcTemplate的原因则是有些关联查询确实用JdbcTemplate要灵活些。
IcopRule VS AOP
实体保存前后,可能需要做其它的操作,比如保存后同时保存附件,比如删除前校验是否能删除。目前系统中都是通过在dao(Activity)中添加IcopRule来实现的。
首先,像保存附件和校验等业务操作,我认为应该提到service层处理;其次,共性操作直接采用aop方式处理即可。