DDD(JPA)设计和代码军规

代码规范

  • 包名请用单数
  • 资源名称一般用单数
  • 资源URL用复数
  • 整体变量和方法的命名请使用驼峰规范
  • 使用lombok,取代Getter,Setter和toString
  • 实体可以统一继承AbstractEntity
  • 实体的ID生成器,可以使用工具类里面的生成器,SnowflakeIdGenerator或者UUIDGenerator
  • request.XXXDTO用于数据的接收和字段校验, response.XXXDTO可以统一使用ResponseDTO封装.
  • 应用层包括(Rest或者Controller和DTO),业务层包括(Service,实体和资源库接口),基础设施层(具体的技术实现)

Restful风格

场景 动作 路径 响应
创建一个标准 POST /api/v1/standards/ 201, 400, 401, 403,5XX
获取标准列表 GET /api/v1/standards/ 200, 400, 401, 403, 5XX
获取某一个标准详情 GET /api/v1/standards/{standard_id} 200, 401, 403, 5XX
创建一个政策 POST /api/v1/policies 201, 400, 401, 403, 5XX
获取政策列表 GET /api/v1/policies 200, 400, 401, 403, 5XX
搜索一个政策 GET /api/v1/policies?name=XXX&pageSize=20 200, 400, 401, 403, 5XX
获取一个政策详情 GET /api/v1/policies/{policy_id} 200, 400, 401, 403, 5XX
报备一个活动 POST /api/v1/activities/ 201, 400, 401, 403, 5XX
获取活动列表 GET /api/v1/activities/ 200, 400, 401, 403, 5XX
修改一个活动 PUT /api/v1/activities/{activity_id} 200, 400, 401, 403, 5XX
获取一个活动详情 GET /api/v1/activities/{activity_id} 200, 400, 401, 403, 5XX

聚合根原则

  • 聚合:由一组相关的领域对象组成,需要通过聚合根来进行统一的导航。
  • 一个聚合根对应的一个资源库。
  • 聚合根之间的引用必须通过ID,而不是对象引用。
    • 聚合根之间如果是多对多的关系,可以将其分解为一对多的关系,然后由一方存放一个List ids;
    • 如果要持久化,需要通过一个wrapper来包装成一个对象,然后以Json字符串存储;
    • 为了更好的实现面向对象,需要实现一个converter,来做对象和Json字符串的互转。
  • 实体不一定是聚合根,聚合根一定是实体。

强制检测

  • Checkstyle编码风格。
  • 提交日志检测。e.g:“[Your name] commit messages”。
  • 测试覆盖率检测, 分支和语句覆盖率大于80%。
  • Push前,强制运行mvn clean package。

JPA Inheritance 关系

  • @MappedSuperclass: 普通的继承用法,既公用的字段放在父类里面,但是父类不是一张表,每个子类都是一张表.
  • @Inheritance(strategy = InheritanceType.SINGLE_TABLE),单表多态问题,同一张 Table,表示了不同的对象,通过一个字段来区分什么对象。
  • 多表多态,每一个子类一张表,父类的表拥有所有公用字段。通过 @Inheritance(strategy = InheritanceType.JOINED) 注解完成,父类和子类都是表,有公用的字段在父表里面;
  • Object的继承,数据库里面表是每一张分开的,相互独立不影响。通过 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 注解完成,父类(可以是一张表,也可以不是)和子类都是表,相互之间没有关系。

JPA 中 N+1 条 SQL 的问题

当我们使用@ManyToOne、@OneToMany、@ManyToMany的时候,之后出现 N+1条SQL的问题,
解决方案:

  • @EntityGraph
    • 多个一对多的时候,需要配合Set集合,以及重写Hashcode和equals方法
    • 在资源库方法上面,增加类似注释: @EntityGraph(attributePaths = {"many1", "many2"})
  • @Query

实体关系问题

  • 双向关系和单一关系,请使用双向关系。
  • 在使用双向关系的时候,一般由多的一方来做关系的主导,少的一般做mappedBy
  • @ManytoOne,@OneToMany配合@JoinColumn使用
  • @ManyToMany配合@JoinTable使用
  • 映射关系请统一写到字段上面,使用lombok取代了所有的getter和setter

级联策略

  • @ManyToMany的时候,一定不能使用CascadeType.ALL
  • 在不清楚级联策略的时候,不要轻易使用CascadeType.Delete
  • 当我们使用关联操作的时候,建议大家先配置成cascade = CascadeType.PERSIST,级联更新或者级联删除的时候比较危险,建议考虑清楚后再使用。

序列化栈溢出问题

  • JsonIgnore, 解决循环序列化问题
  • ToString(exclude=), 解决ToString循环问题
  • JsonIgnoreProperties