您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

开发框架搭建考量

时间:2020-11-16 11:44:17  来源:  作者:

Version:0.9 Starthtml:0000000105 EndHTML:0000064633 StartFragment:0000000141 EndFragment:0000064597

 

本文梳理了在搭建开发框架时的一些考量及具体的处理方法。

 

框架搭建目标

  • 整体代码规范化
  • 重复代码自动化
  • 复杂关系精简化
  • 公共代码统一化

尽量保证开发人员的核心关注点在业务逻辑。

尽量避免非业务问题影响开发进度。

整体代码规范化

规范的作用不是为了规范而规范,也不是对不按规范的人做出惩罚。是为了方便沟通

  • 方便新员工能根据规范文档快速熟悉代码结构
  • 方便接手其他人的代码时,只需要了解业务就可以
  • 方便和其他人沟通时,不需要关注业务之外的内容

可以从下面几个方面做出规范:

  • 统一格式化
    • 一种方案是使用相同的IDE,一种方案是使用相同的格式化配置
    • 保证在代码合并或代码review时,不会因为代码格式化问题导致冲突或难以对比
  • 统一三方库的使用
    • 统一使用一种功能的库,比如:持久化框架选定了Mybatis就不要再用Hibernate,安全框架选定了SpringSecurity就不要使用Shiro
    • 这会增加团队成员的学习成本,以及后续的维护成本
  • 统一代码风格
    • 命名方式的统一:命名实际上是个很重要但是一直不被重视的工作。好的命名能极大的降低沟通成本。至少要保证包层级的名称的语义。我接触的项目里,对DTO来说,有的项目里叫PO,有的叫VO,导致一个开发人员接收另一个项目时,理解上就有了难度。
    • 职责区分:SRP是一个看起来简单,但是很难做好的设计原则。比如:还是拿DTO来说,有的项目直接会直接将Entity作为DTO来使用,可以避免DTO与Entity直接的数据字段处理。实际上这里的DTO即做了DTO的工作,又做了Entity的工作。逻辑简单时是比较爽,但是因为两个对象的职责不一样,进化频率也不一样。当DTO字段调整时,就会对后面的DAO操作产生影响。
    • RESTful规范化:现在大部分的项目都会使用RESTful。如果使用了RESTful,那就按照RESTful的规范来。尽量提高代码语义,不要使用个四不像。比如:就使用POST和GET。
    • 非业务字段独立:业务相关对象和非业务相关对象区分开。例如:对于查询来说,可能需要进行分页。对于分页控制字段建议整合为PageInfo对象来统一处理,而不要作为独立的字段加到DTO对象里。一是不方便管理,二是将不同场景的字段混合到了一起,不易于区分。
    • 接口携带版本号:接口携带版本号,可以基于版本来进行平滑升级。例如:可以保留/api/user/v1/login,同时发布/api/user/v2/login,待/api/user/v1/login不再使用后,在删除/api/user/v1/login
  • 代码架构匹配
    • 架构设计时,实际触及到的是系统、子系统、层级、模块;而具体到代码,只体现了系统(一个个的服务)和层级(Controller,Service,DAL)。模块的映射关系消失了
    • 为了提高代码与架构的匹配度,降低沟通成本。可以在代码层面通过添加一层包的方式,来映射模块关系。
  • 接口与实现的层级关系(推荐)
    • 传统MVC框架是按照Controller、Service和DAO的方式来分层的。如果有接口,一般情况下接口定义与实现是在同一层的,比如Service和ServiceImpl,都在Service层。实际上此种结构是有问题的。
    • 假设现在有两套Service的实现,代码在项目中如何存储?
    • 实际上Service和ServiceImpl分属不同的层,Service是接口层,ServiceImpl是实现层(虽然可能感觉不符合常识,不过确实如此)。两套ServiceImpl对应到Maven项目中,就是两个模块。当要替换实现时,通过Maven的项目结构调整依赖即可。

重复代码自动化

在代码规范化的前提下,基于代码生成工具(比如IDEA的EasyCode)构建一套完整的代码模板,基于代码模板快速的生成对应的代码。提高开发效率。

对于库表结构调整后对代码的影响,可以基于代码结构的调整来尽量减少对现有代码的影响。假设使用Mybatis作为持久化框架,有三个方案:

  • 基于MybatisPlus(其实就是接口继承,具体参考MybatisPlus文档)
  • 基于MybatisProvider
  • 基于MApper接口继承

基于MybatisProvider

  • 自动生成Mapper和Provider,Mapper不使用XML,而使用注解的方式来使用SQL
  • Provider中的方法对参数进行反射来处理字段信息,构建对应的sql
  • 当表结构调整后,只需要调整对应的Bean的字段即可

// Mapper

@SelectProvider(type = IssueProvider.class, method = "list")

Page<IssueVO> list(IssueVO issue);

// Provider

// Issue是对应的Bean,字段调整后,在Issue中添加对应的字段即可(可以生成,可以手动添加)

public String list(Issue issue) {

SQL sql = new SQL();

sql.SELECT("*");

sql.FROM("issue");

BeanMap beanMap = BeanMap.create(issue);

for (Object key : beanMap.keySet()) {

Object val = beanMap.get(key);

if (val != null) {

if (val instanceof String && ((String) val).contains("%")) {

sql.WHERE("`" + FieldUtils.camelToLine(key + "") + "`" + "like #{" + key + "}");

} else {

sql.WHERE("`" + FieldUtils.camelToLine(key + "") + "`" + "=#{" + key + "}");

}

}

}

return sql.toString();

}

  • 优势:
    • 改动量比较小
  • 劣势:
    • 反射对性能的影响
    • 基于注解的方式,习惯性问题
    • IDEA对Provider的方式的支持没有XML高,XML支持自动完成,Provider中不支持

基于Mapper接口继承

  • Mybatis生成的Mapper和XML文件分别为独立的目录
  • 自定义的Mapper继承生成的Mapper,在自定义Mapper中新增自定义接口方法,对应的sql添加到对应的自定义XML文件中
  • 自定义XML文件可以基于NameSpace来使用生成的XML文件的Result
  • 当表结构调整后,重新生成对应的Mapper和XML即可
  • 优势:
    • 符合习惯
  • 劣势:
    • 改动量相对多一点,不过是生成的,所以基本可以忽略

复杂关系精简化

一般项目中的库表设计基本都是按照关联表的方式进行设计的:

  • 主表+子表的关联关系
  • 表中冗余其它表的字段

可以结合场景考虑,做一些结构简化和结构化。简化为对单表的操作,可以基于上面的模板来自动生成代码。

主表+子表的关联关系

此种方式适用于主表和子表需要单独修改的场景。如果主表和子表是聚合关系,即子表依赖于主表存在,且需要一起调整,甚至子表不需要调整,实际可以简化此种关联关系。

因为此种关联关系涉及到了联表查询,联表查询是无法基于工具生成的。通过简化此种关系,可以基于工具来提高开发效率。

表中冗余其它表的字段

有些情况下,可能会将两个逻辑上分离的对象整合为一个对象来处理,简化操作负责度。例如订单中可能直接就包含购买的应用信息。此种情况实际是为了操作的便利性,同时兼顾数据库特性,将结构化的数据扁平化了。

数据扁平化本身问题不大,不过弱化了代码语义。在正常理解里,订单包含订单明细,订单明细中是商品信息。

可以通过下面的方法来解决上面提到的两个问题。

解决方案

MySQL5.7开始支持Json。上述两种情况,都可以基于Json的方式来处理。即数据库字段可以使用json类型。

// 支持基于json的查询,请自行google

create table order

(

......

item_info_json json not null comment 'json',

);

public class Order {

private ItemInfo itemInfoJson;

}

在Mybatis层面,通过TypeHandler来处理Json与对象之间的自动转换。

public interface OrderDao {

// 配置转换handler

@Results(id = "jsonResult", value = {

@Result(property = "itemInfoJson", column = "item_info_json", typeHandler = JsonTypeHandler.class)

})

@Select("select * from order where rec_id = #{recId}")

Order selectByPrimaryKey(@Param("recId") String recId);

@InsertProvider(type = OrderProvider.class, method = "insert")

void insert(Order model);

 

......

}

 

public class OrderProvider {

public String insert(Order order) {

SQL sql = new SQL();

sql.INSERT_INTO("order");

BeanMap beanMap = BeanMap.create(order);

for (Object key : beanMap.keySet()) {

Object val = beanMap.get(key);

if (val != null) {

if ((key + "").endsWith("Json")) { // 根据后缀判定是否需要转换

sql.VALUES("`" + FieldUtils.camelToLine(key + "") + "`", "#{" + key + ", typeHandler=com.iwhalecloud.common.mybatis.JsonTypeHandler}");

} else {

sql.VALUES("`" + FieldUtils.camelToLine(key + "") + "`", "#{" + key + "}");

}

}

}

return sql.toString();

}

转换类处理逻辑:

public class JsonTypeHandler<T> extends BaseTypeHandler<T> {

private static final Gson gson = new Gson();

private Class<T> clazz;

public JsonTypeHandler(Class<T> clazz) {

if (clazz == null) {

throw new IllegalArgumentException("Type argument cannot be null");

} else {

this.clazz = clazz;

}

}

public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {

ps.setString(i, this.toJson(parameter));

}

public T getNullableResult(ResultSet rs, String columnName) throws SQLException {

return this.toObject(rs.getString(columnName), this.clazz);

}

public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {

return this.toObject(rs.getString(columnIndex), this.clazz);

}

public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {

return this.toObject(cs.getString(columnIndex), this.clazz);

}

private String toJson(T object) {

try {

return object instanceof String ? (String)object : gson.toJson(object);

} catch (Exception var3) {

throw new RuntimeException(var3);

}

}

private T toObject(String content, Class<?> clazz) {

if (content != null && !content.isEmpty()) {

try {

return clazz == String.class ? content : gson.fromJson(content, clazz);

} catch (Exception var4) {

throw new RuntimeException(var4);

}

} else {

return null;

}

}

}

公共代码统一化

对于多个项目中使用到的代码,需要根据场景进行公共化统一管理,避免公共代码散落在各个服务中,难以维护。

根据场景的不同,可以有几种管理方式:

  • 基于公共jar包的管理
  • 基于git源代码的管理
  • 基于公共服务的管理

基于公共jar包的管理

此方案是最常规的方案,如果几个服务中使用到了公共的代码,比如:一些工具类。这种情况下就可以将这些类独立为公共项目,通过jar包的方式来进行管理。

基于git源代码的管理

如果公共的代码修改频率比较高,可以基于git的subtree来处理公共代码的管理问题。

  • 创建一个项目,用于存放需要公用的代码,正常创建即可
  • 在需要使用同步代码的项目中执行如下命令(只需要执行一次):

# module是取的别名

git remote add -f module ${上面的项目git地址}

# 将这个项目拉取到 src/module目录下

git subtree add --prefix=src/module module master --squash

  • 在公共项目中修改模块代码后,正常push即可
  • 需要同步的项目,执行如下命令(如果需要同步共享代码,则执行):

git subtree pull --prefix=src/module module master

开发框架搭建考量

 

基于公共服务的管理

如果公用的逻辑是一个独立的功能,后续可以作为服务对外提供服务。那可以考虑将这些代码独立为服务来对外提供服务。

总结

本文从几个维度来考量在搭建一个项目框架时需要考虑的问题,以及对应的解决方案。



Tags:开发框架   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
React 简介 React 基本使用<div id="test"></div><script type="text/javascript" src="../js/react.development.js"></script><script type="text/javascript" src="../js...【详细内容】
2021-11-30  Tags: 开发框架  点击:(19)  评论:(0)  加入收藏
一、搭建环境1、创建数据库表和表结构create table account(id INT identity(1,1) primary key,name varchar(20),[money] DECIMAL2、创建maven的工程SSM,在pom.xml文件引入...【详细内容】
2021-11-11  Tags: 开发框架  点击:(29)  评论:(0)  加入收藏
一、Vue框架的开发流程介绍 当我们从github上下载一个前端模板框架到本地后,框架中经常会自带有一些跳转显示类的功能,我们可以通过查看这些功能是如何实现的,进而一步步改造为...【详细内容】
2021-11-03  Tags: 开发框架  点击:(34)  评论:(0)  加入收藏
直奔主题,今天我要给大家分享的基于ASP.NETCore开源二次开发框架就是YiShaAdmin。YiShaAdmin 是一款基于.NET Core Web + Bootstrap的企业级快速后台开发框架。内置模块如:用...【详细内容】
2021-07-19  Tags: 开发框架  点击:(144)  评论:(0)  加入收藏
自微软宣布 .NET 5 平台消息之后, 相关的快速开发框架 就如 雨后春笋 般的多了起来,众所周知,框架好不好,其 wiki 真的非常重要,好的 wiki 能让人 更加快速 的上手,并体验 起来 F...【详细内容】
2021-04-06  Tags: 开发框架  点击:(379)  评论:(0)  加入收藏
01 智能合约开发框架TOP3下面这三个智能合约开发框架是适用于所有人的。当然,它们各自在功能和侧重上又略有不同,大家可以自行选择:1.Hardhat (JavaScript)2.Truffle (JavaScript)3...【详细内容】
2021-03-29  Tags: 开发框架  点击:(188)  评论:(0)  加入收藏
接下来我应该学习什么?如果你是一名开发人员,这个问题应该一直在你的脑海中。每天都有新的技术问世,也有对现有技术的改进。由于我们无法学习所有这些技术,所以决定下一步应该...【详细内容】
2020-12-25  Tags: 开发框架  点击:(190)  评论:(0)  加入收藏
Version:0.9 StartHTML:0000000105 EndHTML:0000064633 StartFragment:0000000141 EndFragment:0000064597 本文梳理了在搭建开发框架时的一些考量及具体的处理方法。 框架...【详细内容】
2020-11-16  Tags: 开发框架  点击:(96)  评论:(0)  加入收藏
定位拒绝CRUD。用尽可能简单的方式,完成尽可能多的需求。通过约定的方式 实现统一的标准。告别加班,拒绝重复劳动,远离搬砖概述"Rocket-API" 基于spring boot 的API敏捷开发框...【详细内容】
2020-11-06  Tags: 开发框架  点击:(134)  评论:(0)  加入收藏
之前我分享过一款基于ASP.NET MVC + Layui 的通用后台快速开发框架elight.mvc,受到了很多朋友的关注。今天我将给大家分享一款基于.NET Core最新版+Bootstrap的通用后台快速...【详细内容】
2020-09-21  Tags: 开发框架  点击:(1136)  评论:(0)  加入收藏
▌简易百科推荐
近日只是为了想尽办法为 Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI 。然而不断的 Google 过程中偶然间发现了...【详细内容】
2021-12-23  Python阿杰    Tags:FastAPI   点击:(6)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  java老人头    Tags:框架   点击:(11)  评论:(0)  加入收藏
今天来梳理下 Spring 的整体脉络啦,为后面的文章做个铺垫~后面几篇文章应该会讲讲这些内容啦 Spring AOP 插件 (了好久都忘了 ) 分享下 4ye 在项目中利用 AOP + MybatisPlus 对...【详细内容】
2021-12-07  Java4ye    Tags:Spring   点击:(14)  评论:(0)  加入收藏
&emsp;前面通过入门案例介绍,我们发现在SpringSecurity中如果我们没有使用自定义的登录界面,那么SpringSecurity会给我们提供一个系统登录界面。但真实项目中我们一般都会使用...【详细内容】
2021-12-06  波哥带你学Java    Tags:SpringSecurity   点击:(18)  评论:(0)  加入收藏
React 简介 React 基本使用<div id="test"></div><script type="text/javascript" src="../js/react.development.js"></script><script type="text/javascript" src="../js...【详细内容】
2021-11-30  清闲的帆船先生    Tags:框架   点击:(19)  评论:(0)  加入收藏
流水线(Pipeline)是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术。本文主要介绍了诞生于云原生时代的流水线框架 Argo。 什么是流水线?在计算机...【详细内容】
2021-11-30  叼着猫的鱼    Tags:框架   点击:(21)  评论:(0)  加入收藏
TKinterThinter 是标准的python包,你可以在linx,macos,windows上使用它,你不需要安装它,因为它是python自带的扩展包。 它采用TCL的控制接口,你可以非常方便地写出图形界面,如...【详细内容】
2021-11-30    梦回故里归来  Tags:框架   点击:(26)  评论:(0)  加入收藏
前言项目中的配置文件会有密码的存在,例如数据库的密码、邮箱的密码、FTP的密码等。配置的密码以明文的方式暴露,并不是一种安全的方式,特别是大型项目的生产环境中,因为配置文...【详细内容】
2021-11-17  充满元气的java爱好者  博客园  Tags:SpringBoot   点击:(25)  评论:(0)  加入收藏
一、搭建环境1、创建数据库表和表结构create table account(id INT identity(1,1) primary key,name varchar(20),[money] DECIMAL2、创建maven的工程SSM,在pom.xml文件引入...【详细内容】
2021-11-11  AT小白在线中  搜狐号  Tags:开发框架   点击:(29)  评论:(0)  加入收藏
SpringBoot开发的物联网通信平台系统项目功能模块 功能 说明 MQTT 1.SSL支持 2.集群化部署时暂不支持retain&will类型消 UDP ...【详细内容】
2021-11-05  小程序建站    Tags:SpringBoot   点击:(55)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条