目录

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、代码目录

 

ddd分层架构的java代码结构 ddd实现_ddd分层架构的java代码结构

1、接口层(interfaces层)

接口层是跟前端交互的桥梁,要做两个动作

1)利用组装器assembler实现DTO与DO的转换

2)编写Restful的API接口,类似controller编写

1.1、利用组装器assembler实现DTO与DO的转换

先创建DTO:

interfaces--assembler--dto--LeaveDTO.java

ddd分层架构的java代码结构 ddd实现_ddd分层架构的java代码结构_02

 然后 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或者其他参数来传参。

反例:

ddd分层架构的java代码结构 ddd实现_java_03

正例:

 

ddd分层架构的java代码结构 ddd实现_spring_04

3.3、仓储实现

domain-repository持久化

持久化之前先完成DO->PO转换,然后再仓储中实现PO对象的持久化(domain-service-LeaveDomainService中实现转换):

我们可以将Leave中的多个值对象采用【属性嵌入】的方式放入到一个Leave PO中。

ddd分层架构的java代码结构 ddd实现_值对象_05

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

代码:

ddd分层架构的java代码结构 ddd实现_spring_06

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所有层,包括三方工具、配置、通用方法等等

ddd分层架构的java代码结构 ddd实现_值对象_07