Gleasy平台开发经验总结

目录
1. 前言
2. 数据库开发
2.1. 数据库
2.2. 数据表
2.3. 数据字段
3. 服务端开发
3.1. 实体层
3.2. 数据访问层
3.3. 服务层
3.4. 缓存层
3.5. 索引层
3.6. 控制层
3.7. 接口层
4. 前端开发
4.1. 数据访问层
4.2. 组件化开发
4.3. 组件服务化
4.4. 数据缓存层
4.5. 定时器
5. 结束语

1. 前言

1) 基础平台:Gleasy开发平台

2) 目标:经验 -> 规范 -> 软件工程化

2. 数据库开发

针对MySql

2.1. 数据库

2.1.1. 命名规范

1) 如:systemCircle-boot、systemCircle-global、systemCircle-local-0、systemCircle-local-1

2.2. 数据表

2.2.1. 命名规范

1) 如: card、card_extra_info

2.3. 数据字段

2.3.1. 命名规范

1) 如: driver_class_name

2) 唯一主键:id

2.3.2. 类型规范

1) 标识:bigint(20)

2) 字符串:varchar(32)、varchar(64) 、varchar(128)、 varchar(256) 、 varchar(512) 、 varchar(1024) 、 varchar(2048) 、 varchar(4096)

3) 布尔:tinyint(1)

4) 日期:datetime、date、time

5) 数值:tinyint(1)、smallint(5)

6) 其他:有待补充

2.3.3. 其他约束

1) 不允许使用外键(数据关联,是业务层干的活)。

2) 一般情况下,只有一个主键,而且就是字段名就是id。

3. 服务端开发

3.1 实体层(Domain)

3.1.1 实体类

与数据库表一一对应的类。类名称对应表名称,类属性对应表字段。

3.1.1.1 包规范

Source Folder:src/main/java

如:com.gleasy.person.domain、com.gleasy.gcd.domain

3.1.1.2 命名规范

如:Card.java、CardExtraInfo.java

(直接对应数据库表card、card_extra_info)

3.1.2 实体属性

3.1.2.1 命名规范

如:id、userId、birthdayType

3.1.2.2 类型规范

与数据库类型一一对应(虽然不是很严谨,但能解决大多数问题)

1)    标识:java.lang.Long

2)    字符串:java.lang.String

3)    布尔:java.lang.Byte(没错,不是boolean。0:false;1:true)

4)    日期:java.util.Date、java.sql.Date、java.sql.Time

5)    数值:java.lang.Byte、java.lang.Integer

来个映射表,直观些:

数据库 实体类
bigint(20) java.lang.Long
varchar java.lang.String
tinyint(1) java.lang.Byte
datetime java.util.Date
Date java.sql.Date
Time java.sql.Time
smallint(5) java.lang.Integer

3.1.3. 实体包装类

顾名思义,给实体类增加一些字段,方便在应用层使用。

3.1.3.1. 包规范

Source Folder:src/main/java

如:com.gleasy.person.bean

3.1.3.2. 命名规范

如:CardWrap.java、CardExtraInfoWrap.java

(当然拉,Card和CardWrap最好是继承关系)

3.1.4. IBatis映射文件

3.1.4.1. 包规范

Source Folder:src/main/resources

如:com.gleasy.person.domain、com.gleasy.gcd.domain

(与实体类包路径一致)

3.1.4.2. 命名规范

如:Card.map.xml、CardExtraInfo.map.xml

(前缀与类名一致)

3.1.4.3. 内容范例

3.1.4.4. 自动化

满足以上规范,实体类&Ibatis映射文件,甚至可通过自动化代码生成。^_^开心吧…

3.2. 数据访问层(Dao)

3.2.1. 包规范

接口如:com.gleasy.person.dao

实现如:com.gleasy.person.dao.ibatis

3.2.2. 类命名规范

接口如:CardDAO.java、CardExtraInfoDAO.java

实现如:CardDAOImpl.java、CardExtraInfoDAOImpl.java

(与实体类Card.java、CardExtraInfo.java一一对应)

3.2.3. 继承

接口需继承BaseLocalDAO,如:public interface CardDAO extends BaseLocalDAO<Card>

实现需继承BaseLocalDAOImpl,如:public class CardDAOImpl extends BaseLocalDAOImpl<Card> implements CardDAO

继承最直接的好处,就是不用自己实现get、save、delete、update方法。

3.2.4. 增单个对象

BaseLocalDAOImpl实现的save方法,但没有包含id的生成规则。所以一般情况下需要自己定义新的save方法。

3.2.4.1. 方法命名规范

如:saveCard,对应实体类Card。

3.2.4.2. 实例

3.2.5. 删单个对象

BaseLocalDAOImpl已经实现了。

3.2.6. 改单个对象

BaseLocalDAOImpl已经实现了。

3.2.7. 查单个对象

BaseLocalDAOImpl已经实现了。

3.2.8. 查多个对象

自定义方法,一般只允许根据多个id查询。其他复杂查询通过缓存、索引平台、索引库实现。

3.2.9. 其他操作

不允许

3.3. 服务层(Service)

3.3.1. 包规范

接口如:com.gleasy.person.service

实现如:com.gleasy.person.service.impl

(person代表项目标识)

3.3.2. 类命名规范

3.3.2.1. 按业务命名

如:ImportService表示导入业务功能,ExportService表示导出业务功能,CardService表示名片相关的功能。

ImportServiceImpl、ExportServiceImpl、CardServiceImpl是实现类。

3.3.3. 上下层关联

指Service调用DAO。

一般情况下,一个Service只能调用一个DAO,比如CardService只能调用CardDAO。

(反过来也就是,一般情况下,一个DAO都必将被一个Service所包)

3.3.4. 同层关联

指Service调用Servcie。

允许一个Service调用多个Service,这很常用。

3.3.5. 纯业务Service

也就是那些不直接调用DAO的Service。

3.3.6. 方法参数问题

3.3.6.1. 参数暴露

一般情况下,采用这种方式。直观,容易维护。

如:… 方法(参数1, 参数1, … , 参数N) throws BusinessLevelException;

3.3.6.2. 参数封装

如:… 方法(实体对象1, 实体对象2, … , 实体对象N) throws BusinessLevelException;

非迫不得已,禁止如下方式:

… 方法(Map1, Map2 , … , MapN) online casino throws BusinessLevelException;

3.4. 缓存层(Cache)

基本思想,将缓存的功能代码独立开来,并提供接口给服务层调用。

缓存一般非持久化,有失效时间。

3.4.1. 包命名规范

如:com.gleasy.person.cache、com.gleasy.person.cache.impl

3.4.2. 类命名规范

一般每个DAO对应一个Cache,如CardDAO对应CardCache、CardCacheImpl

3.4.3. 主要方法

3.4.3.1. put

将对象放入缓存

3.4.3.2. get

在缓存查询对象

3.4.3.3. remove

删除缓存对象

3.4.3.4. mget

批量查询缓存(按ID)

3.4.4. 服务层调用缓存

3.4.4.1. 增

写库 -> 写缓存

3.4.4.2. 删

删库 -> 删缓存

3.4.4.3. 改

先查 -> 改库 -> 写缓存

3.4.4.4. 查

查缓存 -> 不在缓存则查库 -> 不在缓存则写缓存

3.4.5. 持久化缓存

除非主动删除,否则永不失效。需根据具体业务进行设计,这里不做具体介绍。

3.5. 索引层(Index)

调用全文索引平台的能力,实现复杂查询。

需索引平台提供了相应的接口实现索引的创建和查询。对于应用来说,主要是实现接口的二次封装,以方便服务层调用。

3.5.1. 包命名规范

如:com.gleasy.person.index、com.gleasy.person.index.impl

3.5.2. 类命名规范

如:CardIndex、CardIndexImpl

3.5.3. 更新索引

如:public void update(Card card)

3.5.4. 删除索引

public void remove(Card card)

3.5.5. 清除索引

public void clear()

3.5.6. 查索引

public List<Map> search(参数1, 参数2, … , 参数N , Integer pageNum, Integer pageSize)

pageNum:第几页,从1开始

pageSize:每页大小

3.5.7. 查询计数

public Integer count(参数1, 参数2, … , 参数N)

3.6. 控制层(Action)

负责接收过滤用户参数,并调用恰当的服务解决问题,最后以Json格式返回结果。

3.6.1. 包命名规范

如:com.gleasy.person.card.openapi.action

person:项目标识

card:业务划分

openapi:标识公开(一般去掉表示内网调用)

3.6.2. 类命名规范

3.6.2.1. 增

如:SaveCardAction

3.6.2.2. 删

如:DeleteCardsByCardIdsAction

3.6.2.3. 改

如:UpdateAvatarAction

3.6.2.4. 查

如:GetCardsByCardIdsAction

3.6.3. 上下层关联

也就是Action层调用Servcie层。

一般一个Action只会调用一个Service。

3.6.4. 配置文件

按业务分离,一般一个包对应一个配置文件。

如:com.gleasy.person.card.openapi.action对应applicationContext-controller-card.xml

当然了,这个包下的Action,都需要加上前缀/card/

而且这个也需要对应前端的CardModel

3.6.5. 其他

不要把业务的东西放在Action层。难维护,难复用功能。

复杂的参数解析,特别前端的Json格式的内容解析,可以放在Service层处理。

3.7. 接口层(Api)

以Jar包的方式,对控制层进行封装,方便其他项目调用。如常用的GcdApi、UserInfoClient。

4. 前端开发

4.1. 数据访问层(Model)

4.1.1. 实例

4.1.2. 包规范

如:com.gleasy.attendance.model

(这里的attendance一般指项目标识,可能多级,如:com.gleasy.a.b.c.attendance.model)

4.1.3. 命名规范

类名:ManagerModel(对应applicationContext-controller-manager.xml)

变量名/方法名:createManagersAction/createManagers(对应CreateManagersAction.java)

4.1.4. 参数规范

一般情况下function(x, y, z, success, error, token)

x,y,z是业务级参数,success,error,token是框架级参数(独立开参数的考虑是增强可读性、可维护性)

特殊情况下function(object, success, error, token)

object包含大量(一般>10个)参数,如:card就有40个字段,object一般是对应实体类。

防止传”null”字符串到后台

这段代码是必须的。


4.1.5. 单例类

4.1.6. 调用方式

4.2. 组件化开发(components)

先按照业务功能、系统功能、页面区域划分组件。

4.2.1. 组件定义

最简单的组件由一个JS文件和一个HTML文件组成。如部门的下拉列表组件就由:DepartmentSelector.class.js和DepartmentSelector.html组成(名称需要一致,便于阅读)。
html文件中JS组件需要的所有html元素(不包含子组件需要的html元素)。

4.2.1.1. 实例



4.2.2. 组件交互
4.2.2.1. Notify机制

每个组件都有notify方法,用于监听事件。
组件通过container直接获取应用句柄,调用notify,要求传递消息。
顶级组件(container)自顶向下,逐层下发消息(下发过程中不允许改变消息名称)。
组件收到消息后,自行决定是否处理or向下传播。
通过parent直接获取直接父组件句柄,进而调用父组件notify方法(比较少用)。

4.2.2.2. 注册监听机制

高度解耦,但维护成本过高,不推荐。

4.2.2.3. 延迟回调机制

一般情况下,组件间的相互调用,结果都需要以回调的方式获取较为靠普。但如果调用的时候,对方组件还没有完成自身初始化工作,咋办?延迟回调即可,代码如下:



4.3. 组件服务化(Daemon)

如果您的组件需要被跨项目调用,组件服务化是最佳选择。
注册服务(略)。
调用服务:


4.4. 数据缓存层(Cache)

基本思想:不直接调用Model,而是调用Cache。Cache判断是否前端有缓存且未过期,满足条件使用前端缓存,否则调用Model,并更新缓存。

4.5. 定时器
4.5.1. 循环任务(setInterval)

4.5.1.1. setInterval陷阱

经典案例:一说频繁发状态变更消息,导致收发双方系统崩溃。
根本原因:多次并发调用了setInterval。
解决办法:setInterval之前要先clearInterval,要不然代码被异步调用多次,之前的就永远也清除不了,成死循环了。

4.5.2. 定时任务(setTimeout)

5. 结束语

以上经验,是个人在实际的研发过程中不断探索并汲取其他同事经验中的精华部分,最后总结出来的,但仍不免存在纰漏,期待您的批评意见(dingyong@gleasy.net)。

此条目发表在 Java技术, 分布式技术, 前端技术, 数据库技术 分类目录。将固定链接加入收藏夹。

发表评论