【内容指引】
领域驱动开发;
云端生成项目代码;
项目代码结构介绍。
一、领域驱动开发
1.”云开发“开发者社交平台概述
“云开发”开发者社交平台是一个基于互联网云端协作、进行软件项目管理、代码开发、全球招标、在线交付等为一体的开发者社交平台。
整个产品包含产品、项目、代码、测试、文档及团队六个模块,采用Spring Cloud微服务的方式开发,每个模块对应一个微服务。本系列文章以“文档”这个模块的微服务代码为例,探讨”云开发“平台微服务项目的测试驱动开发最佳实践,希望能抛砖引玉。
2.”文档“模块介绍
“文档”这个模块的使用场景如下:
1>.项目经理可以为项目创建及维护文档分类;
2>.该项目的成员可以为项目添加文档。
3.设计领域类:
领域类仅需声明私有字段,“云开发”平台的代码生成器会自动生成完整的领域类数据(私有字段、构造函数、get及set属性)。
文档分类(Category)
package top.cloudev.entity; /** * Created by mac.manon on 2018/4/2. */ public class Category {//文档分类 private Long categoryId;//文档分类ID private Long projectId;//文档分类所属的项目 private String name;//分类名称 private Integer sequence;//排序 private int DeletionAudited; }
文档(Document)
package top.cloudev.entity; /** * Created by mac.manon on 2018/4/2. * docType:文档类型,1=文件,2=链接 * url:文档访问网址,当类型是文件是该字段为Null */ public class Document {//文档 private Long documentId;//文档ID private Long categoryId;//文档所属分类 private String name;//文档名称 private Short docType;//文档类型 private String url;//文档访问网址 private String memo;//文档描述 private String accessory;//附件 private int DeletionAudited; }
4.关于审计字段
上面每个领域类中最后都定义了一个审计字段“DeletionAudited”:
private int DeletionAudited;
所谓审计字段,就是每个领域类中包含的创建时间、创建人员、修改时间等信息,审计字段的用法有五种:
无审计、基本审计、创建审计、修改审计和删除审计。下面用Team这个领域类为例,展示用不同审计字段生成的代码区别:
无审计
即,不定义审计字段。
package top.cloudev.entity; /** * Created by mac.manon on 2018/4/2. */ public class Category {//文档分类 private Long categoryId;//文档分类ID private Long projectId;//文档分类所属的项目 private String name;//分类名称 private Integer sequence;//排序 }
“云开发”平台生成的成品领域类代码如下:
package top.cloudev.doc.domain; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.validator.constraints.*; import javax.persistence.*; import javax.validation.constraints.*; import javax.validation.constraints.Pattern; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import java.util.UUID; /** * Category(文档分类) 的领域层 * Created by Mac.Manon on 2018/04/02 */ @Entity @DynamicUpdate(true) @Table(name="tbl_category") public class Category implements Serializable { private static final long serialVersionUID = 1L; public interface CheckCreate{}; public interface CheckModify{}; /** * 文档分类ID */ @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long categoryId; /** * 文档分类所属的项目 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "project_id") private Long projectId; /** * 分类名称 */ @NotBlank(groups={CheckCreate.class, CheckModify.class}) @Length(min = 1, max = 50, groups={CheckCreate.class, CheckModify.class}) //@Pattern(regexp = "", groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "name", length = 50) private String name = ""; /** * 排序 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "sequence") private Integer sequence; /** *TODO 请将数据表的名称及字段名称加入到国际化语言包中: TableName.category=u6587u6863u5206u7c7b FieldName.category.categoryId=u6587u6863u5206u7c7bID FieldName.category.projectId=u6587u6863u5206u7c7bu6240u5c5eu7684u9879u76ee FieldName.category.name=u5206u7c7bu540du79f0 FieldName.category.sequence=u6392u5e8f * * *Tip: *如果后续加入引用类型字段,可考虑使用@Valid注解; *如果后续加入Collection、Map和数组类型字段,可考虑使用@Size(max, min)注解; */ /** *空构造函数 * */ public Category(){ super(); } /** *带参构造函数 * */ public Category(Long projectId,String name,Integer sequence){ super(); this.projectId = projectId; this.name = name; this.sequence = sequence; } /** *Getter,Setter * */ public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Long getProjectId() { return projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSequence() { return sequence; } public void setSequence(Integer sequence) { this.sequence = sequence; } @Override public String toString() { return "Category [categoryId=" + categoryId + "," + "projectId=" + projectId + "," + "name=" + name + "," + "sequence=" + sequence + "]"; } }
基本审计(HasCreationTime)
即,在原定义的领域类字段基础上自动加上创建时间(creationTime)这个字段。
package top.cloudev.entity; /** * Created by mac.manon on 2018/4/2. */ public class Category {//文档分类 private Long categoryId;//文档分类ID private Long projectId;//文档分类所属的项目 private String name;//分类名称 private Integer sequence;//排序 private int HasCreationTime; }
“云开发”平台生成的成品领域类代码如下:
package top.cloudev.doc.domain; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.validator.constraints.*; import javax.persistence.*; import javax.validation.constraints.*; import javax.validation.constraints.Pattern; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import java.util.UUID; /** * Category(文档分类) 的领域层 * Created by Mac.Manon on 2018/04/02 */ @Entity @DynamicUpdate(true) @Table(name="tbl_category") public class Category implements Serializable { private static final long serialVersionUID = 1L; public interface CheckCreate{}; public interface CheckModify{}; /** * 文档分类ID */ @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long categoryId; /** * 文档分类所属的项目 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "project_id") private Long projectId; /** * 分类名称 */ @NotBlank(groups={CheckCreate.class, CheckModify.class}) @Length(min = 1, max = 50, groups={CheckCreate.class, CheckModify.class}) //@Pattern(regexp = "", groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "name", length = 50) private String name = ""; /** * 排序 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "sequence") private Integer sequence; /** * 创建时间 */ @Column(nullable = false, name = "creation_time", updatable=false) private Date creationTime = new Date(); /** *TODO 请将数据表的名称及字段名称加入到国际化语言包中: TableName.category=u6587u6863u5206u7c7b FieldName.category.categoryId=u6587u6863u5206u7c7bID FieldName.category.projectId=u6587u6863u5206u7c7bu6240u5c5eu7684u9879u76ee FieldName.category.name=u5206u7c7bu540du79f0 FieldName.category.sequence=u6392u5e8f * * *Tip: *如果后续加入引用类型字段,可考虑使用@Valid注解; *如果后续加入Collection、Map和数组类型字段,可考虑使用@Size(max, min)注解; */ /** *空构造函数 * */ public Category(){ super(); } /** *带参构造函数 * */ public Category(Long projectId,String name,Integer sequence,Date creationTime){ super(); this.projectId = projectId; this.name = name; this.sequence = sequence; this.creationTime = creationTime; } /** *Getter,Setter * */ public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Long getProjectId() { return projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSequence() { return sequence; } public void setSequence(Integer sequence) { this.sequence = sequence; } public Date getCreationTime() { return creationTime; } public void setCreationTime(Date creationTime) { this.creationTime = creationTime; } @Override public String toString() { return "Category [categoryId=" + categoryId + "," + "projectId=" + projectId + "," + "name=" + name + "," + "sequence=" + sequence + ",creationTime=" + creationTime + "]"; } }
创建审计(CreationAudited)
即,在基本审计的基础上加上创建者(creatorUserId)这个字段。
package top.cloudev.entity; /** * Created by mac.manon on 2018/4/2. */ public class Category {//文档分类 private Long categoryId;//文档分类ID private Long projectId;//文档分类所属的项目 private String name;//分类名称 private Integer sequence;//排序 private int CreationAudited; }
“云开发”平台生成的成品领域类代码如下:
package top.cloudev.doc.domain; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.validator.constraints.*; import javax.persistence.*; import javax.validation.constraints.*; import javax.validation.constraints.Pattern; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import java.util.UUID; /** * Category(文档分类) 的领域层 * Created by Mac.Manon on 2018/04/02 */ @Entity @DynamicUpdate(true) @Table(name="tbl_category") public class Category implements Serializable { private static final long serialVersionUID = 1L; public interface CheckCreate{}; public interface CheckModify{}; /** * 文档分类ID */ @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long categoryId; /** * 文档分类所属的项目 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "project_id") private Long projectId; /** * 分类名称 */ @NotBlank(groups={CheckCreate.class, CheckModify.class}) @Length(min = 1, max = 50, groups={CheckCreate.class, CheckModify.class}) //@Pattern(regexp = "", groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "name", length = 50) private String name = ""; /** * 排序 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "sequence") private Integer sequence; /** * 创建时间 */ @Column(nullable = false, name = "creation_time", updatable=false) private Date creationTime = new Date(); /** * 创建者 */ @Column(nullable = false, name = "creator_user_id", updatable=false) private long creatorUserId; /** *TODO 请将数据表的名称及字段名称加入到国际化语言包中: TableName.category=u6587u6863u5206u7c7b FieldName.category.categoryId=u6587u6863u5206u7c7bID FieldName.category.projectId=u6587u6863u5206u7c7bu6240u5c5eu7684u9879u76ee FieldName.category.name=u5206u7c7bu540du79f0 FieldName.category.sequence=u6392u5e8f * * *Tip: *如果后续加入引用类型字段,可考虑使用@Valid注解; *如果后续加入Collection、Map和数组类型字段,可考虑使用@Size(max, min)注解; */ /** *空构造函数 * */ public Category(){ super(); } /** *带参构造函数 * */ public Category(Long projectId,String name,Integer sequence,Date creationTime,long creatorUserId){ super(); this.projectId = projectId; this.name = name; this.sequence = sequence; this.creationTime = creationTime; this.creatorUserId = creatorUserId; } /** *Getter,Setter * */ public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Long getProjectId() { return projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSequence() { return sequence; } public void setSequence(Integer sequence) { this.sequence = sequence; } public Date getCreationTime() { return creationTime; } public void setCreationTime(Date creationTime) { this.creationTime = creationTime; } public long getCreatorUserId() { return creatorUserId; } public void setCreatorUserId(long creatorUserId) { this.creatorUserId = creatorUserId; } @Override public String toString() { return "Category [categoryId=" + categoryId + "," + "projectId=" + projectId + "," + "name=" + name + "," + "sequence=" + sequence + ",creationTime=" + creationTime + ",creatorUserId=" + creatorUserId + "]"; } }
修改审计(ModificationAudited)
即,在创建审计的基础上加上最近修改时间(lastModificationTime)和最近修改者(lastModifierUserId)这两个字段。
package top.cloudev.entity; /** * Created by mac.manon on 2018/4/2. */ public class Category {//文档分类 private Long categoryId;//文档分类ID private Long projectId;//文档分类所属的项目 private String name;//分类名称 private Integer sequence;//排序 private int ModificationAudited; }
“云开发”平台生成的成品领域类代码如下:
package top.cloudev.doc.domain; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.validator.constraints.*; import javax.persistence.*; import javax.validation.constraints.*; import javax.validation.constraints.Pattern; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import java.util.UUID; /** * Category(文档分类) 的领域层 * Created by Mac.Manon on 2018/04/02 */ @Entity @DynamicUpdate(true) @Table(name="tbl_category") public class Category implements Serializable { private static final long serialVersionUID = 1L; public interface CheckCreate{}; public interface CheckModify{}; /** * 文档分类ID */ @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long categoryId; /** * 文档分类所属的项目 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "project_id") private Long projectId; /** * 分类名称 */ @NotBlank(groups={CheckCreate.class, CheckModify.class}) @Length(min = 1, max = 50, groups={CheckCreate.class, CheckModify.class}) //@Pattern(regexp = "", groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "name", length = 50) private String name = ""; /** * 排序 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "sequence") private Integer sequence; /** * 创建时间 */ @Column(nullable = false, name = "creation_time", updatable=false) private Date creationTime = new Date(); /** * 创建者 */ @Column(nullable = false, name = "creator_user_id", updatable=false) private long creatorUserId; /** * 最近修改时间 */ @Column(name = "last_modification_time") private Date lastModificationTime; /** * 最近修改者 */ @Column(name = "last_modifier_user_id") private long lastModifierUserId; /** *TODO 请将数据表的名称及字段名称加入到国际化语言包中: TableName.category=u6587u6863u5206u7c7b FieldName.category.categoryId=u6587u6863u5206u7c7bID FieldName.category.projectId=u6587u6863u5206u7c7bu6240u5c5eu7684u9879u76ee FieldName.category.name=u5206u7c7bu540du79f0 FieldName.category.sequence=u6392u5e8f * * *Tip: *如果后续加入引用类型字段,可考虑使用@Valid注解; *如果后续加入Collection、Map和数组类型字段,可考虑使用@Size(max, min)注解; */ /** *空构造函数 * */ public Category(){ super(); } /** *带参构造函数 * */ public Category(Long projectId,String name,Integer sequence,Date creationTime,long creatorUserId,Date lastModificationTime,long lastModifierUserId){ super(); this.projectId = projectId; this.name = name; this.sequence = sequence; this.creationTime = creationTime; this.creatorUserId = creatorUserId; this.lastModificationTime = lastModificationTime; this.lastModifierUserId = lastModifierUserId; } /** *Getter,Setter * */ public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Long getProjectId() { return projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSequence() { return sequence; } public void setSequence(Integer sequence) { this.sequence = sequence; } public Date getCreationTime() { return creationTime; } public void setCreationTime(Date creationTime) { this.creationTime = creationTime; } public long getCreatorUserId() { return creatorUserId; } public void setCreatorUserId(long creatorUserId) { this.creatorUserId = creatorUserId; } public Date getLastModificationTime() { return lastModificationTime; } public void setLastModificationTime(Date lastModificationTime) { this.lastModificationTime = lastModificationTime; } public long getLastModifierUserId() { return lastModifierUserId; } public void setLastModifierUserId(long lastModifierUserId) { this.lastModifierUserId = lastModifierUserId; } @Override public String toString() { return "Category [categoryId=" + categoryId + "," + "projectId=" + projectId + "," + "name=" + name + "," + "sequence=" + sequence + ",creationTime=" + creationTime + ",creatorUserId=" + creatorUserId + ",lastModificationTime=" + lastModificationTime + ",lastModifierUserId=" + lastModifierUserId + "]"; } }
删除审计(ModificationAudited)
即,在修改审计的基础上加上已删除(isDeleted)、删除时间(deletionTime)和删除者(deleterUserId)这三个字段。注意,应用了删除审计的代码才用非物理删除,否则采用物理删除!
package top.cloudev.entity; /** * Created by mac.manon on 2018/4/2. */ public class Category {//文档分类 private Long categoryId;//文档分类ID private Long projectId;//文档分类所属的项目 private String name;//分类名称 private Integer sequence;//排序 private int DeletionAudited; }
“云开发”平台生成的成品领域类代码如下:
package top.cloudev.doc.domain; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.validator.constraints.*; import javax.persistence.*; import javax.validation.constraints.*; import javax.validation.constraints.Pattern; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import java.util.UUID; /** * Category(文档分类) 的领域层 * Created by Mac.Manon on 2018/04/02 */ @Entity @DynamicUpdate(true) @Table(name="tbl_category") public class Category implements Serializable { private static final long serialVersionUID = 1L; public interface CheckCreate{}; public interface CheckModify{}; /** * 文档分类ID */ @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long categoryId; /** * 文档分类所属的项目 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "project_id") private Long projectId; /** * 分类名称 */ @NotBlank(groups={CheckCreate.class, CheckModify.class}) @Length(min = 1, max = 50, groups={CheckCreate.class, CheckModify.class}) //@Pattern(regexp = "", groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "name", length = 50) private String name = ""; /** * 排序 */ //@Range(min=value,max=value, groups={CheckCreate.class, CheckModify.class}) @Column(nullable = false, name = "sequence") private Integer sequence; /** * 创建时间 */ @Column(nullable = false, name = "creation_time", updatable=false) private Date creationTime = new Date(); /** * 创建者 */ @Column(nullable = false, name = "creator_user_id", updatable=false) private long creatorUserId; /** * 最近修改时间 */ @Column(name = "last_modification_time") private Date lastModificationTime; /** * 最近修改者 */ @Column(name = "last_modifier_user_id") private long lastModifierUserId; /** * 已删除 */ @Column(nullable = false, name = "is_deleted") private Boolean isDeleted = false; /** * 删除时间 */ @Column(name = "deletion_time") private Date deletionTime; /** * 删除者 */ @Column(name = "deleter_user_id") private long deleterUserId; /** *TODO 请将数据表的名称及字段名称加入到国际化语言包中: TableName.category=u6587u6863u5206u7c7b FieldName.category.categoryId=u6587u6863u5206u7c7bID FieldName.category.projectId=u6587u6863u5206u7c7bu6240u5c5eu7684u9879u76ee FieldName.category.name=u5206u7c7bu540du79f0 FieldName.category.sequence=u6392u5e8f * * *Tip: *如果后续加入引用类型字段,可考虑使用@Valid注解; *如果后续加入Collection、Map和数组类型字段,可考虑使用@Size(max, min)注解; */ /** *空构造函数 * */ public Category(){ super(); } /** *带参构造函数 * */ public Category(Long projectId,String name,Integer sequence,Date creationTime,long creatorUserId,Date lastModificationTime,long lastModifierUserId,Boolean isDeleted,Date deletionTime,long deleterUserId){ super(); this.projectId = projectId; this.name = name; this.sequence = sequence; this.creationTime = creationTime; this.creatorUserId = creatorUserId; this.lastModificationTime = lastModificationTime; this.lastModifierUserId = lastModifierUserId; this.isDeleted = isDeleted; this.deleterUserId = deleterUserId; this.deletionTime = deletionTime; } /** *Getter,Setter * */ public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public Long getProjectId() { return projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSequence() { return sequence; } public void setSequence(Integer sequence) { this.sequence = sequence; } public Date getCreationTime() { return creationTime; } public void setCreationTime(Date creationTime) { this.creationTime = creationTime; } public long getCreatorUserId() { return creatorUserId; } public void setCreatorUserId(long creatorUserId) { this.creatorUserId = creatorUserId; } public Date getLastModificationTime() { return lastModificationTime; } public void setLastModificationTime(Date lastModificationTime) { this.lastModificationTime = lastModificationTime; } public long getLastModifierUserId() { return lastModifierUserId; } public void setLastModifierUserId(long lastModifierUserId) { this.lastModifierUserId = lastModifierUserId; } public Boolean getIsDeleted() { return isDeleted; } public void setIsDeleted(Boolean isDeleted) { this.isDeleted = isDeleted; } public Date getDeletionTime() { return deletionTime; } public void setDeletionTime(Date deletionTime) { this.deletionTime = deletionTime; } public long getDeleterUserId() { return deleterUserId; } public void setDeleterUserId(long deleterUserId) { this.deleterUserId = deleterUserId; } @Override public String toString() { return "Category [categoryId=" + categoryId + "," + "projectId=" + projectId + "," + "name=" + name + "," + "sequence=" + sequence + ",creationTime=" + creationTime + ",creatorUserId=" + creatorUserId + ",lastModificationTime=" + lastModificationTime + ",lastModifierUserId=" + lastModifierUserId + ",isDeleted=" + isDeleted + ",deletionTime=" + deletionTime + ",deleterUserId=" + deleterUserId + "]"; } }
二、云端生成项目代码:
将上述文档分类(Category)和文档(Document)这两个领域类打包成zip压缩包,上传到“云开发”平台,点击“生成微服务项目代码”,“云开发”平台会根据上传的领域类批量生成微服务初始化代码。代码生成完毕后,点击“打包下载”即可。
在正式进行Spring Cloud微服务项目测试驱动开发之前,请在Github上获取本教程所需的初始化项目代码:
Github代码获取:https://github.com/MacManon/top_cloudev_doc
三、项目代码结构介绍