目录
0、代码目录
1、接口层(interfaces层)
1.1、利用组装器assembler实现DTO与DO的转换
1.2、编写Restful的API接口,类似controller编写
2、应用层(application层)
3、领域层(domain层)
3.1、聚合根、实体、值对象
3.1.1、聚合根
3.1.2、实体
3.1.3、值对象
3.2、领域服务
3.3、仓储实现
3.4、领域事件
4、基础层(infrastrucure层)
0、代码目录
1、接口层(interfaces层)
接口层是跟前端交互的桥梁,要做两个动作
1)利用组装器assembler实现DTO与DO的转换
2)编写Restful的API接口,类似controller编写
1.1、利用组装器assembler实现DTO与DO的转换
先创建DTO:
interfaces--assembler--dto--LeaveDTO.java
然后 interfaces--assembler--LeaveAssembler.java
/**
* 转换 DTO->DO
* 或者 DO->DTO
*
* @author steven
* @date 2021/08/01
*/
public class LeaveAssembler {
//完成DO到DTO转换
public static LeaveDTO toDTO(Leave leave) {
val leaveDTO = new LeaveDTO();
//转换过程略......
return leaveDTO;
}
//完成DO到DTO转换
public static Leave toDO(LeaveDTO leaveDTO) {
val leave = new Leave();
//转换过程略......
return leave;
}
}
1.2、编写Restful的API接口,类似controller编写
interfaces-facade--Leave API.java
/**
* @author steven
* @date 2021/08/01
*/
@Slf4j
@RestController
public class LeaveApi {
@Autowired
LeaveApplicationService leaveApplicationService;
/**
* 创建请假单
*
* @param leaveDTO
* @return
*/
@GetMapping("/createLeave")
public ResultVO createLeave(LeaveDTO leaveDTO) {
log.info("----接口层");
//1.DTO转成DO
val leaveDo = LeaveAssembler.toDO(leaveDTO);
//2.执行应用层的创建业务逻辑
leaveApplicationService.createLeaveinfo(leaveDo);
return ResultVO.ok();
}
/**
* 查询请假单
*
* @param id
* @return
*/
@GetMapping("/queryLeave")
public ResultVO queryLeave(Integer id) {
//1.根据id查请假单
Leave leaveDO = leaveApplicationService.queryLeave(id);
//2.DO转成DTO输出
LeaveDTO leaveDTO = LeaveAssembler.toDTO(leaveDO);
return ResultVO.ok(leaveDTO);
}
}
2、应用层(application层)
应用层主要是完成领域服务的组合和编排,是很薄的一层。
注意:跟领域服务中一样,也要尽量避免调用其他聚合的实体或者值对象,这种操作会增加聚合的耦合度。我们可以通过ID或者其他参数来传参。
application--service--LeaveApplicationService.java
/**
* @author steven
* @date 2021/08/01
*/
@Slf4j
@Service
public class LeaveApplicationService {
@Autowired
LeaveDomainService leaveDomainService;
@Autowired
PersonDomainService personDomainService;
/**
* @Description: 创建请假
* @Author: steven
* @Date: 2021/8/1
*/
public void createLeaveinfo(Leave leave) {
log.info("----应用层");
//1、Person聚合-获取申请人的信息
Person person = personDomainService.find(null == leave.getApplicant() ? 1 : leave.getApplicant().getId());
leave.getApplicant().setName(person.getName());
//2、Leave聚合-创建请假单
leaveDomainService.create(leave);
}
/**
* @param id
* @return
* @Description: 查询请假单
*/
public Leave queryLeave(Integer id) {
Leave leave = leaveDomainService.query(id);
return leave;
}
}
3、领域层(domain层)
3.1、聚合根、实体、值对象
3.1.1、聚合根
Leave--entity--Leave.java
Leave聚合根作为实体,采用充血模式,建议聚合根自身的业务在聚合根类方法中实现,涉及到多个实体组合的业务在领域服务中完成。
/**
* 请假聚合根
*
* @author steven
* @date 2021/07/31
*/
@Data
public class Leave {
String id;//聚合根ID
Date startTime;
Date endTime;
long duration;
Applicant applicant;//申请人值对象
Approver approver;//审批人值对象
ApprovalInfo approvalInfo;//实体,审批意见实体
List<ApprovalInfo> hisApprovalInfos;
Status status;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public long getDuration() {
return endTime.getTime() - startTime.getTime();
}
public Leave addHistoryApprovalInfo(ApprovalInfo hisApprovalInfo) {
if (null == hisApprovalInfos) {
hisApprovalInfos = new ArrayList<>();
}
this.hisApprovalInfos.add(hisApprovalInfo);
return this;
}
public Leave create() {
this.setStatus(Status.APPROVALING);
return this;
}
}
3.1.2、实体
Leave--entity-ApprovaInfo.java
审批意见实体被Leave聚合根引用,他有自己的属性和值对象。
/**
* 审批意见实体
*
* @author steven
* @date 2021/07/31
*/
public class ApprovalInfo {
String approvalInfoId;//实体ID
Approver approver;//值对象
ApprovalType approvalType;//值对象
String msg;
long time;
}
3.1.3、值对象
Leave--entity--valueObject--Approver.java 审批人值对象
/**
* 审批人值对象
*
* @author steven
* @date 2021/07/31
*/
public class Approver {
String personId;//此ID为聚合根Person的id
String personName;//审批人姓名
int level;//领导级别
}
Status.java 状态值对象
/**
* 状态值对象
* @author steven
* @date 2021/07/31
*/
public enum Status {
APPROVALING, APPROVED, REJECTED
}
注意:值对象只能做整体的替换、不可修改,所以值对象很少有修改或者新增的方法。值对象的构建或初始化,可以通过聚合根的构造函数或者工厂模式完成。
3.2、领域服务
领域服务和实体方法(例如3.1.1里面的方法)的区别:实体方法完成自身的业务逻辑(简单的原子逻辑),领域服务完成多个实体组合成的相对复杂的业务逻辑。
在一个聚合内可以设计一个领域服务类,管理聚合内的所有领域服务。
domain--service--LeaveDomainService.java 请假聚合的领域服务
/**
* 请假聚合的领域服务类
*
* @author steven
* @date 2021/07/31
*/
@Service
public class LeaveDomainService {
//仓储接口,面向仓储接口编程
@Autowired
LeaveRepository leaveRepository;
//工厂,实现DO与PO的相互转换
@Autowired
LeaveFactory leaveFactory;
//聚合内的事务控制
@Transactional
public void create(Leave leave) {
//do->po
LeavePO leavePO = leaveFactory.toPO(leave);
//其他业务逻辑
//to do ...
leaveRepository.save(leavePO);
}
public Leave query(Integer id) {
val byId = leaveRepository.findById(id);
LeavePO leavePO = byId.get();
//po->do
Leave leave = leaveFactory.toDO(leavePO);
return leave;
}
}
以上实现了很多内容
1)用工厂模式实现了DO的创建和数据初始化
2)仓储模式实现DO对象持久化
注意:领域服务中,我们尽量避免调用其他聚合的领域服务或其他聚合的实体或者值对象,这种操作会增加聚合的耦合度。我们可以通过ID或者其他参数来传参。
反例:
正例:
3.3、仓储实现
domain-repository持久化
持久化之前先完成DO->PO转换,然后再仓储中实现PO对象的持久化(domain-service-LeaveDomainService中实现转换):
我们可以将Leave中的多个值对象采用【属性嵌入】的方式放入到一个Leave PO中。
JPA接入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ddd
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
代码:
LeavePO.java/**
* @author steven
* @date 2021/07/31
*/
@Data
@Entity
@Table(name = "t_leave")
public class LeavePO {
@Id //主键id
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键生成策略
@Column(name = "id")//数据库字段名
private Integer id;
Date startTime;
Date endTime;
long duration;
@Column
private String approverId;
@Column
private String name;
@Enumerated(EnumType.STRING)
Status status;
}LeaveRepository.java@Repository
public interface LeaveRepository extends JpaRepository<LeavePO, Integer> {
}
3.4、领域事件
暂时没有实现
案例:to do
4、基础层(infrastrucure层)
贯串DDD所有层,包括三方工具、配置、通用方法等等