如何搭建合适的Web框架?

之前在Web开发框架推导一文中我们一步步的搭建了一个开发框架 。

如何搭建合适的Web框架?

文章插图
 
在当时的情况下 , 还算满足需求 。但是随着项目的逐渐完善 , 需求变更的频度逐渐变得比新增需求的频度高 , 原来框架的弊端越来越明显 , 所以需要对框架进行升级改进 。
我们先来看原来框架的问题 , 然后基于这些问题 , 来对框架进行改进 。
原框架的问题
  • 代码生成问题
  • 参数传递问题
  • Service层问题
  • 测试依赖问题
  • MApper.xml的问题
代码生成问题
在原框架中 , 我们基于各种约束 , 编写了一个代码生成组件 , 通过这个组件 , 我们可以针对选中的表来生成Controller,Service,Model,Mapper等一系列的类 , 也就是说 , 只要建完表 , 就可以直接生成一套CRUD , 直接就可以启动并测试 。这在项目初期看起来很美 , 但是在需求变动时 , 还是有很多的局限性 。
首先 , 生成的代码逻辑是固化的 。如果稍微有些调整 , 就需要调整生成代码的组件 , 然后重新打包 , 上传到jar仓库 , 项目修改组件版本 , 再进行代码生成 , 整个流程过于繁琐 。
其次 , 为了方便代码的生成 , 其实是做了不少妥协的:
  • 为了方便在修改表字段以后 , 能够重新生成 , 很多类都抽象了一个基类用于操作Model字段 。这些基类不能够手动修改 , 因为每次生成都会覆盖 。这实际导致了类的数量的增多 。
  • 生成的CRUD固化了 , 不能手动调整 。如果生成的CRUD不满足需求 , 不能直接在代码上修改 。只能拷贝一份进行修改 , 因为再次生成时会覆盖 。这导致了代码的冗余 。
  • Param和Result委托了Model , 这在Model发生改变时 , 能在编译期就能知道对应字段的调整 。但是也引入了不少问题 , 我们在「参数传递问题」一节单独讨论 。
参数传递问题
当初为了便于代码的生成 , 决定Param和Result都继承Model , 这导致了如下的一些问题:
  • 使得Param和Result都依赖了Model 。但是Param和Result是视图层模型 , 而Model是持久层模型 , 两者的进化度并不是一致的 。但是现在的继承关系导致了在默认情况下视图层模型的进化需要和持久层同步 , 当然你也可以手动调整Param和Result , 但是这又导致了代码生成的优势没有了 。
  • Param和Result通过委托的方式来设置字段 , 也就是说 , 它们实际是没有字段的 , 通过getter和setter将值设置到了Model中 。这就没法使用lombok来简化getter和setter , 使得Param和Result代码行数较多
  • 同时 , 对于swagger来说 , 有些注解需要基于字段 , 导致某些功能无法实现(例如:ModelAttribute) , 只能基于额外手段来处理(例如:需要通过ApiImplicitParams来实现字段文档) 。
  • CRUD都是基于同一个Param和Result , 导致前端的接口会显示很多无用的字段 , 加大前端理解接口的难度
Service层问题
Service层有如下问题:
  • Service层的职责过重 , 包括了事务处理、参数设置、业务逻辑
  • 导致Service中的代码是面条代码 , 不利于业务逻辑的理解
  • 同时事务注解是直接加在类上的 , Spring的默认事务机制会导致类似如下代码的逻辑调用不会抛出期望的异常
// PostServicepublic String savePost(Post post) { postRepository.save(post); for(PostDiscuss discuss : post.getDiscuss()) { // 这里是抓不到RuntimeException异常的 , 会是一个TransactionRollBack的异常 discussService.save(discuss); }}// discussServicepublic String savePost(PostDiscuss discuss) {throw new RuntimeException("保存失败");}测试依赖问题
核心的业务逻辑在Service中 , 测试还是需要依赖于Spring , 当项目越来越大时 , 启动项目的时间越来越长 , 可能要1分钟甚至更长 。这就导致单元测试效率越来越低 。
Mapper.xml的问题
在面试的时候 , 我经常会问下面的一些问题: