DDD领域驱动设计,开发框架落地

发布时间:2024年01月22日

开局一张图

这是一张之前为了让开发人员理解DDD开发框架的maven依赖关系图,整个项目大概包含这么多的module和依赖关系,其中让大家比较疑惑的可能是infrastructure层为什么要依赖domain,这里有一个控制反转,把最不容易发生变化且业务最核心的的部分放在依赖的最顶层。

真实的项目结构如下:

关于父pom和common中的内容就不做介绍了,下面把api、client、domain、infrastructure、app层的代码详细贴一下,以student为例,也仅仅是个分层的示例,实际业务比这个要复杂的多。

api模块

api模块主要对提供对外部服务提供api接口的能力,只是服务和服务之间的api,不包括正常和前端、移动端的api。其中包括接口和dto两部分

DTO

@Getter
@Setter
@ToString
public class DeStudentDTO extends AbstractTenantBizDto<Long, Long> {
    /**
     * 学生ID
     */
    @ApiModelProperty(value = "学生参数ID")
    private Long paramId;

    /**
     * 学生姓名
     */
    @ApiModelProperty("学生参数名称")
    private String paramName;

    /**
     * 学生年龄
     */
    @ApiModelProperty("学生参数年龄")
    private Integer paramAge;

    /**
     * 学生性别
     */
    @ApiModelProperty("学生参数性别")
    private String paramGender;

    /**
     * 职业
     */
    @ApiModelProperty("职业")
    private String occupation;
    /**
     * 学校
     */
    @ApiModelProperty("学校")
    private String school;

    /**
     * 籍贯
     */
    @ApiModelProperty("籍贯")
    private String area;

    /**
     * 家庭详细住址
     */
    @ApiModelProperty("详细住址")
    private String address;
    /**
     * 生日
     */
    @ApiModelProperty("生日")
    private Date birthday;
    /**
     * 爱好
     */
    @ApiModelProperty("爱好")
    private String hobby;
    /**
     * 学习经历
     */
    @ApiModelProperty("学习经历")
    private List<ExperienceDTO> experiences = new ArrayList<>();
    /**
     * 附件ids
     * 使用英文逗号分割
     */
    @ApiModelProperty("附件ids")
    private String fileIds;

    /**
     * 高级搜索
     */
    @ApiModelProperty("高级搜索")
    private String seniorSearchColumn;


    @Override
    public Long getId() {
        return paramId;
    }

    @Override
    public void setId(Long along) {
        setParamId(along);
    }
}

Facade

@RequestMapping("/facade/deStudentParam")
public interface DeStudentFacade {

    @GetMapping("/getAllStudents")
    List<DeStudentResultDTO> getAllStudents();
}

app模块

app模块为应用层,实现对外接口、业务逻辑,编排领域能力

Controller

@Api(value = "学生管理", tags = "学生管理")
@RestController
@Slf4j
@Validated
@RequestMapping("/data/SeStudentParam")
public class DeStudentController {

    @Autowired
    private  DeStudentApplicationService studentApplicationService;


    @GetMapping("/page")
    public Page<DeStudentResultDTO> selectPage(Page page,DeStudentDTO studentDTO) {

        DeStudentBo studentBo = DeStudentAssembler.toBo(studentDTO);
        return studentApplicationService.selectPage(page, studentBo)
                .map(entity -> DeStudentAssembler.toResultDTO(entity));
    }
}

Assembler

public class DeStudentAssembler {
    /**
     * 将领域实体转换为resultDTO
     * @param deStudentBo
     * @return
     */
    public static DeStudentResultDTO toResultDTO(DeStudentBo deStudentBo){
        DeStudentResultDTO dto = DeStudentDTOBOMapper.INSTANCE.toDeStudentResultDTO(deStudentBo);
        List<ExperienceResultDTO> experienceResultDTOS = deStudentBo.getExperienceBoList()
                .stream().map(entity->{
                    ExperienceResultDTO resultDTO = DeStudentDTOBOMapper.INSTANCE.toExperienceResultDTO(entity);
                    if (!ObjectUtils.isEmpty(resultDTO.getPeriod())){
                        resultDTO.setPeriods(resultDTO.getPeriod().split(","));
                    }
                    return resultDTO;
                }).collect(Collectors.toList());
        dto.setExperiences(experienceResultDTOS);
        return dto;
    }

    /**
     * 将DTO转换为领域实体
     * @param dto
     * @return
     */
    public static DeStudentBo toBo(DeStudentDTO dto){
        DeStudentBo deStudentBo = DeStudentDTOBOMapper.INSTANCE.toDeStudentBo(dto);
        // 教育经历处理
        List<ExperienceBo> experienceBoList = dto.getExperiences()
                .stream().map(entity->{
                    ExperienceBo bo = DeStudentDTOBOMapper.INSTANCE.toExperienceBo(entity);
                    if (entity.getPeriods().length == 2){
                        bo.setPeriod(entity.getPeriods()[0] + "," + entity.getPeriods()[1]);
                    }
                    return bo;
                }).collect(Collectors.toList());
        deStudentBo.setExperienceBoList(experienceBoList);
        // 高级查询处理
        String seniorSearchColumn = dto.getSeniorSearchColumn();
        if (!StringUtils.isEmpty(seniorSearchColumn)){
            // 解析字符串
            seniorSearchColumn = StringEscapeUtils.unescapeHtml(seniorSearchColumn);
            HashMap seniorSearchMap = JSON.parseObject(seniorSearchColumn, HashMap.class);
            deStudentBo.setSeniorSearchMap(seniorSearchMap);
        }
        return deStudentBo;
    }
    public static List<DeStudentResultDTO> toResultDTOS(List<DeStudentBo> deStudentBos){
        List<DeStudentResultDTO> resultDTOS = deStudentBos.stream().map(e->toResultDTO(e)).collect(Collectors.toList());
        return resultDTOS;
    }
}

mapstruct

public interface DeStudentDTOBOMapper {
    DeStudentDTOBOMapper INSTANCE = Mappers.getMapper(DeStudentDTOBOMapper.class);

    /**
     * 学生参数入参dto转换入参的bo
     * @return DeStudentBo
     */
    DeStudentBo toDeStudentBo(DeStudentDTO deStudentDTO);

    /**
     * 学生参数 返回的BO转成返回的DTO
     * @param deStudentBo
     * @return DeStudentResultDTO
     */
    DeStudentResultDTO toDeStudentResultDTO(DeStudentBo deStudentBo);

    /**
     * 学生参数实体集合转换成dto集合
     * @param deStudentBoList
     * @return 学生参数DeStudentResultDTO的集合
     */
    List<DeStudentResultDTO> toDeStudentResultDTOList(List<DeStudentBo> deStudentBoList);

    ExperienceBo toExperienceBo(ExperienceDTO experienceDTO);

    ExperienceResultDTO toExperienceResultDTO(ExperienceBo experienceBo);

}

ApplicationService

@Service
public class CallslipApplicationService {
    private final CallslipDomainService callslipDomainService;

    public CallslipApplicationService(CallslipDomainService callslipDomainService) {
        this.callslipDomainService = callslipDomainService;
    }

    public Long insertCallslip(CallslipBo callslipBo){
        return callslipDomainService.insertCallslip(callslipBo);
    }

    public boolean delCallslip(Long id){
        return callslipDomainService.delCallslip(id);
    }

    public boolean delCallslipByIds(List<Long> ids){
        return callslipDomainService.delCallslipByIds(ids);
    }

    public boolean updateCallslip(CallslipBo callslipBo){
        return callslipDomainService.updateCallslip(callslipBo);
    }

    public CallslipBo getCallslipById(Long id){
        return callslipDomainService.getCallslipById(id);
    }

    public Page<CallslipBo> selectPage(Page page, CallslipBo callslipBo){
        return callslipDomainService.selectPage(page,callslipBo);
    }
    public Page<CallslipBo> selectPageByIds(Page page, CallslipBo callslipBo){
        return callslipDomainService.selectPageByIds(page,callslipBo);
    }
}

domain模块

domain模块为领域层,提供最核心的领域能力

Bo

@Data
@ExcelTarget("deStudentBo")
public class DeStudentBo extends AbstractTenantBizBo<Long, String, Long> {
    /**
     * 学生ID
     */
    private Long paramId;

    /**
     * 学生姓名
     */
    @Excel(name = "学生姓名",needMerge = true)
    private String paramName;
    /**
     * 学生年龄
     */
    @Excel(name = "学生年龄",needMerge = true)
    private Integer paramAge;
    /**
     * 学生性别
     */
    @Excel(name = "学生性别",dict = "sex",needMerge = true)
    private String paramGender;
    /**
     * 职业
     */
    @Excel(name = "职业",dict = "crudDemoOccupation",needMerge = true)
    private String occupation;
    /**
     * 学校(字典项)
     */
    @Excel(name = "学校", dict = "school",needMerge = true)
    private String school;

    /**
     * 籍贯
     */
    @Excel(name = "籍贯",needMerge = true)
    private String area;

    /**
     * 家庭详细住址
     */
    @Excel(name = "家庭详细住址",needMerge = true)
    private String address;
    /**
     * 生日
     */
    @Excel(name = "生日",format = "yyyy-MM-dd",needMerge = true)
    private Date birthday;
    /**
     * 爱好
     */
    @Excel(name = "爱好",needMerge = true)
    private String hobby;
    /**
     * 教育经历
     */
    @ExcelCollection(name = "教育经历")
    List<ExperienceBo> experienceBoList;
    /**
     * 高级查询
     */
    HashMap<String,List<SeniorSearch>> seniorSearchMap;


    @Override
    public Long getId() {
        return paramId;
    }

    @Override
    public void setId(Long along) {
        setParamId(along);
    }
}

Factory

@Component
public class DeStudentFactory {
    /**
     * 将领域实体转换为PO
     * @param deStudentBo
     * @return
     */
    public DeStudent createDeStudentPO(DeStudentBo deStudentBo){
        DeStudent deStudent = DeStudentBOPOMapper.INSTANCE.toDeStudent(deStudentBo);
        List<Experience> experienceList = deStudentBo.getExperienceBoList()
                .stream().map(experienceBo -> DeStudentBOPOMapper.INSTANCE.createExperiencePO(experienceBo))
                .collect(Collectors.toList());
        deStudent.setExperienceList(experienceList);
        return deStudent;
    }

    /**
     * 将PO转换为领域实体
     * @param deStudent
     * @return
     */
    public DeStudentBo getDeStudentBo(DeStudent deStudent){
        DeStudentBo deStudentBo = DeStudentBOPOMapper.INSTANCE.toDeStudentBo(deStudent);
        List<ExperienceBo> experienceBoList = deStudent.getExperienceList()
                .stream().map(e->DeStudentBOPOMapper.INSTANCE.getExperienceBo(e))
                .collect(Collectors.toList());
        deStudentBo.setExperienceBoList(experienceBoList);
        return deStudentBo;
    }

    /**
     * 将PO集合转换为领域实体集合
     * @param deStudents
     * @return 领域实体DeStudentBo集合
     */
    public List<DeStudentBo> getDeStudentBoList(List<DeStudent> deStudents){
        List<DeStudentBo> deStudentBos = deStudents.stream().map(e->getDeStudentBo(e))
                .collect(Collectors.toList());
        return deStudentBos;
    }
}

mapstruct

public interface CallSlipBOPOMapper {
    CallSlipBOPOMapper instance = Mappers.getMapper(CallSlipBOPOMapper.class);

    Callslip toCallslip(CallslipBo callslipBo);

    CallslipBo toCallslipBo(Callslip callslip);

    List<CallslipBo> toCallslipBoList(List<Callslip> callslips);
}

Respository

public interface CallslipRespository {

    Long insertCallslip(Callslip callslip);

    boolean delCallslip(Long id);

    boolean updateCallslip(Callslip callslip);

    Callslip getCallslipById(Long id);

    Page<Callslip> selectPage(Page page,Callslip callslip);

    Page<Callslip> selectPageByIds(Page page, Callslip callslip, List<Long> ids);

    boolean delCallslipByIds(List<Long> ids);

    default  boolean retBool(Integer result) {
        return null != result && result >= 1;
    }
}

Po

@TableName("student")
@Data
public class DeStudent extends AbstractTenantBizEntity<Long, String, Long> {
    /**
     * 学生id
     */
    @TableId(value = "id",type= IdType.ID_WORKER)
    private Long paramId;
    /**
     * 学生姓名
     */
    @TableField("name")
    private String paramName;

    /**
     * 年龄
     */
    @TableField("age")
    private Integer paramAge;

    /**
     * 性别
     */
    @TableField("gender")
    private String paramGender;

    /**
     * 职业
     */
    @TableField("occupation")
    private String occupation;
    /**
     * 学校
     */
    @TableField("school")
    private String school;
    /**
     * 籍贯
     */
    @TableField("area")
    private String area;
    /**
     * 家庭详细住址
     */
    @TableField("address")
    private String address;
    /**
     * 生日
     */
    @TableField("birthday")
    private Date birthday;
    /**
     * 爱好
     */
    @TableField("hobby")
    private String hobby;
    /**
     * 学习经历
     */
    @TableField(value = "experiences",exist = false)
    private List<Experience> experienceList = new ArrayList<>();

    @Override
    public Long getId() {
        return paramId;
    }

    @Override
    public void setId(Long aLong) {
        this.paramId = aLong;
    }
}

DomainService

/**
 * 领域服务,通常是跨实体(单聚合根)使用,但不可跨领域
 */
@Service
@Slf4j
public class DeStudentDomainService {
    private final DeStudentRespository deStudentRespository;
    private final DeStudentFactory deStudentFactory;

    public DeStudentDomainService(DeStudentRespository deStudentRespository, DeStudentFactory deStudentFactory) {
        this.deStudentRespository = deStudentRespository;
        this.deStudentFactory = deStudentFactory;
    }

    /**
     * 增加学生
     */
    public long addStudent(DeStudentBo studentBo){
        return deStudentRespository.insertStudent(deStudentFactory.createDeStudentPO(studentBo));
    }

    /**
     * 删除学生
     */
    public boolean delStudentById(Long sid){
        return deStudentRespository.delStudentById(sid);
    }

    /**
     * 批量删除学生
     * @param ids
     * @return
     */
    public boolean delStudentByIds(List<Long> ids){
        return deStudentRespository.delStudentByIds(ids);
    }
    /**
     * 更新学生信息
     */
    public boolean updateStudentById(DeStudentBo studentBo){
        return deStudentRespository.updateStudentById(deStudentFactory.createDeStudentPO(studentBo));
    }
    /**
     * 查询所有学生
     */
    public List<DeStudentBo> getAllStudents(){
        return deStudentFactory.getDeStudentBoList(deStudentRespository.getAllStudents());
    }
    /**
     * 根据id查找学生
     */
    public DeStudentBo getStudentById(Long sid){
        return deStudentFactory.getDeStudentBo(deStudentRespository.getStudentById(sid));
    }

    public List<DeStudentBo> queryStudents(DeStudentBo studentBo){
        return deStudentFactory.getDeStudentBoList(deStudentRespository.queryStudents(deStudentFactory.createDeStudentPO(studentBo)));
    }

    /**
     * 分页查询
     * @param page
     * @param studentBo
     * @return
     */
    public Page<DeStudentBo> selectPage(Page page, DeStudentBo studentBo) {
        return deStudentRespository.selectPage(page,deStudentFactory.createDeStudentPO(studentBo))
                .map(entity->deStudentFactory.getDeStudentBo(entity));
    }
}

infrastructure模块

infrastructure模块为基础设施层,主要是实现领域层定义的事件接口,通常会做操作数据库、mq等操作。

respository.impl

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class CallslipRespositoryImpl implements CallslipRespository {

    private final CallslipMapper callslipMapper;

    public CallslipRespositoryImpl(CallslipMapper callslipMapper) {
        this.callslipMapper = callslipMapper;
    }


    @Override
    public Long insertCallslip(Callslip callslip) {
         callslipMapper.insert(callslip);
         return callslip.getId();
    }

    @Override
    public boolean delCallslip(Long id) {
        return retBool(callslipMapper.deleteById(id));
    }

    @Override
    public boolean updateCallslip(Callslip callslip) {
        return retBool(callslipMapper.updateById(callslip));
    }

    @Override
    public Callslip getCallslipById(Long id) {
        return callslipMapper.selectById(id);
    }

    @Override
    public Page<Callslip> selectPage(Page page, Callslip callslip) {
        PageUtil.translateSortPropertyToColumn(page,Callslip.class,null);
        return (Page<Callslip>) callslipMapper.selectPage(page,new QueryWrapper<>(callslip));
    }

    @Override
    public Page<Callslip> selectPageByIds(Page page, Callslip callslip, List<Long> ids) {
        PageUtil.translateSortPropertyToColumn(page,Callslip.class,null);
        QueryWrapper<Callslip> queryWrapper = new QueryWrapper<>(callslip);
        queryWrapper.in("id",ids);
        return (Page<Callslip>) callslipMapper.selectPage(page,queryWrapper);
    }

    @Override
    public boolean delCallslipByIds(List<Long> ids) {
        return retBool(callslipMapper.deleteBatchIds(ids));
    }
}

Mapper

public interface DeStudentMapper extends WfBaseMapper<DeStudent> {
}

xml

其实底层操作数据库是用的mybatisplus,这里就不详细贴了,当然,还有很多操作事件的event,有需要的朋友再联系我

文章来源:https://blog.csdn.net/lihongjing/article/details/135749271
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。