关于后台问题的一些思考

来源:互联网 发布:laylive客服系统源码 编辑:程序博客网 时间:2024/05/19 01:32

关于后台问题的一些思考

1. 关于异常

  1. 对于异常应该且必须输出到日志中(不能捕获后不处理),但是不应该直接返回给前台;
  2. 返回给前台的应该是可读的简易信息,并且要注意不要暴露后台保密信息(包括类名、数据库名、文件名、行号等);
  3. 应给为每类异常建立专门的异常类,而不是直接new Exception();
  4. 良好的异常类的设计不应该原封不动的继承Exception,直接调用super函数,而应该在异常类内部构造能够更加清楚的表示异常信息的Message。
public class AccountUnderflowException extends Exception {    private static final long serialVersionUID = -6299588017190080876L;    private Account account;    private BigDecimal amount;    public AccountUnderflowException(Account account, BigDecimal amount) {        this.account = account;        this.amount = amount;    }    public String getMessage() {        return String.format("Not allowed to debit '%s' from account '%s'", amount, account);    }}

注:代码出自http://download.csdn.net/download/angly/8206155

2. 关于返回值为null

  1. 如果确实是由“意外情况”(数据库错误、网络错误等)造成返回null,应给直接抛出异常信息;对于能确定造成空指针异常的,应给封装空指针异常后再进行抛出;
  2. 如果只是因为查询结果为空,那么应该构建空集合进行返回,但是需要注意的是Collections.emptyList()或Collections.EMPTY_LIST,返回的空集合是一个特殊集合,只能进行常见集合的部分操作,使用时要特别注意;
  3. 最容易出现空指针异常的情况:数据库查询、链式调用(对象不为null,对象的某个属性为null)、Java8 Stream、循环(List中可能含有null元素)。
推荐阅读:避免Java应用中NullPointerException的技巧和最佳实践

3. 关于分包

建议为所有的异常类、配置类、工具类、常量类、枚举类对应的建立专门的包进行存放。

4. 关于分层

所谓的分包分层,不过是为了让各个类的职责更清晰,复用代码更方便。

5. 关于Entity、DO、DTO、VO之间的转换

总体上有2中方案:

  1. 构造函数,如VO的一个构造函数为new VO(DTO,service...);
  2. 转换函数,也可以把构造函数转为静态转换函数fromDTO(DTO)或toVO(),或者把所有的DTO2VO的转换都集中写在一个转换类中。

6. 关于DDD(领域驱动设计)

按照官方的说法是:相当于把现有的Service层和Entity合并到一起,在Entity中加入业务方法,使其由贫血模型转变为充血模型。

我的理解是:

  1. 不要合并Service层和Entity,保留原来分层结构;
  2. 在Service层增加DOMAIN,用来取代BO、DO等实体类型,可以把上一条中提到的类型转换函数写在DOMAIN中,在DOMAIN中主要进行一些和实体关系紧密的验证和操作,以减轻service.impl中的函数复杂度;
  3. 实体转换顺序:Param(业务参数组合),Condition(分页排序等参数组合)->DOMAIN(->ENTITY)->VO;
  4. 增删改查,场景,事务等操作都放在Service层,在DOMAIN中只含有对域本身的操作,并且尽量把转换验证等操作都做成静态的,举例:如果在转化的过程中,需要引入其他service或dao,尽量通过作为函数参数引入,而不是类的成员变量,这样一来可以把所有的关联依赖都集中在Service中。DOMAIN中操作都是内存中进行,DOMAIN不可以操作数据库,所以DOMAIN中没有save()等方法,对DOMAIN的持久化应该在Service中进行;
  5. ENTITY和VO基本上都是贫血模型,可以按照数据库、持久化框架、前端界面等要求独立设计和演变;DOMAIN是充血模型,与业务相关,按照业务的要求进行演变;
  6. Service和DOMAIN都应该进行null判断,DOMAIN中的转换函数等本身有自身安全性要求,不能直接遇空报错;Service中的场景走向可能需要根据null判断来决定,切换场景、报错、返回空集合、返回null

7. 关于命名

1. 第一层:表现层,对外服务层,User Interface layer
如果是面向前端网站,包可以用controller、api,类可以用**Controller、**Api;
如果是面向服务,包可以用facade、application、api,类可以用**Facade、**ApiService、**Api;
作为参数传入这层的实体类,一般放在query、condition包下,可以用**Param(业务参数组合)、**Condition(分页排序等参数组合)、**Form(表单参数);
作为返回值传出这层的实体类,一般放在model、vo、dto等包下,可以用**VO(面向前端)、**DTO(面向服务)。

2. 第二层:业务层,业务逻辑层,Business Logic Layer
一般包名 service、business(简写biz),类名可用**Service、**Business(Biz);
这层的实体,一般放在domain包下,为所谓的BO、DOMAIN等从现实生活中提取的对应的业务对象,是在系统中操作起来最方便的实体类,并且负责连接和转换第一层和第三次的实体对象,建议使用不带后缀实体名。

3. 第三层:持久层,数据访问层,Data access layer
一般包名可用dao、repository等,类名可用**Dao、**Repository等;
这层是实体,一般放在entity包下,可以用**Entity。

另外,包名一般为:com.公司/产品线.项目名.各层分类.功能分类,如com.taobao.cainiao.controller.address下面放着和地址相关的接口。

8. 关于发布

如果是面向前端的项目一般打成一个包即可;
如果是面向服务的项目一般打成两个包:
一个包是第一层的接口和实体,可以命名为***-api:
另一个包是剩下的部分,可以命名为***-service。


附录:

  1. 后台模型
    后台模型

  2. 后台目录
    后台目录

原创粉丝点击