DDD分层架构

1. 经典分层架构(四层架构)

DDD分层架构_第1张图片

2.工程目录结构

 map  
    ┣ api  
    ┃ ┣ assembler 
    ┃ ┣ controller
    ┃ ┃ ┣ v1
    ┃ ┣ dto
    ┣ app 
    ┃ ┣ service
    ┃ ┃ ┣ impl
    ┣ config
    ┣ domain 
    ┃ ┣ aggregate 
    ┃ ┣ entity 
    ┃ ┣ factory 
    ┃ ┣ repository
    ┃ ┣ service
    ┣ infra
    ┃ ┣ exception
    ┃ ┣ constant
    ┃ ┣ feign
    ┃ ┣ mapper
    ┃ ┣ repository
    ┃ ┃ ┣ impl
    ┃ ┣ util

3.各层级结构

3.1 api(用户展现层)

负责向用户展现信息,并且解析用户行为
请求应用层以获取要表现的数据
发送命令给应用层执行某个用户的命令

┣ api  
    ┃ ┣ assembler
    ┃ ┃ ┣ Assembler
    ┃ ┣ controller
    ┃ ┃ ┣ v1
    ┃ ┃ ┃ ┣ MappingHeaderController
    ┃ ┃ ┃ ┣ MappingItemController
    ┃ ┃ ┃ ┣ MappingRuleHeaderController
    ┃ ┣ dto
    ┃ ┃ ┣ MappingItemValueDTO
    ┃ ┃ ┣ MappingLineDTO
  • assembler
    负责dto与领域对象之前的相互转化,数据交换
  • controller
    负责定义用户接口
  • dto
    用于接口传参以及前端显示
    数据传输的载体,通过DTO把内部的对象与外界隔离

3.2 app(应用层)

为程序提供任务处理,用于跨领域服务的操作,业务流程的整合
相对于领域层,应用层是很薄的一层,应用层定义了软件要完成的任务,要尽量简单
它做业务流程的整合,为下一层的领域对象协助任务,委托工作
对外,为展现层提供service
对内,调用领域层完成各种业务逻辑

  ┣ app 
    ┃ ┣ service
    ┃ ┃ ┣ MappingHeaderService
    ┃ ┃ ┣ MappingItemService
    ┃ ┃ ┣ MappingLineService
    ┃ ┃ ┣ MappingRuleLineService
    ┃ ┃ ┣ impl
    ┃ ┃ ┃ ┣ MappingHeaderServiceImpl
    ┃ ┃ ┃ ┣ MappingItemServiceImpl
    ┃ ┃ ┃ ┣ MappingLineServiceImpl
    ┃ ┃ ┃ ┣ MappingRuleLineServiceImpl
  • service
    应用服务接口
    尽量薄的一层,主要用来协调领域层和基础层的操作来满足业务流程

3.3 domain(领域层)

主要负责表达业务逻辑,是整个系统的核心层,包括聚合对象,实体,工厂,资源库,领域服务

┣ domain 
    ┃ ┣ aggregate 
    ┃ ┣ entity
    ┃ ┃ ┣ MappingHeader
    ┃ ┃ ┣ MappingItem
    ┃ ┃ ┣ MappingLine
    ┃ ┃ ┣ MappingRuleLine
    ┃ ┣ factory 
    ┃ ┣ repository
    ┃ ┃ ┣ MappingHeaderRepository
    ┃ ┃ ┣ MappingItemRepository
    ┃ ┃ ┣ MappingLineRepository
    ┃ ┃ ┣ MappingRuleLineRepository
    ┃ ┣ service
    ┃ ┃ ┣ MappingDomainService
    ┃ ┃ ┣ MappingLineDomainService
    ┃ ┃ ┣ MappingRuleDomainService
    ┃ ┃ ┣ MappingValueDomainService
    ┃ ┃ ┣ impl
    ┃ ┃ ┃ ┣ MappingDomainServiceImpl
    ┃ ┃ ┃ ┣ MappingLineDomainServiceImpl
    ┃ ┃ ┃ ┣ MappingRuleDomainServiceImpl
    ┃ ┃ ┃ ┣ MappingValueDomainServiceImpl
    ┃ ┣ vo
    ┃ ┃ ┣ MappingItemValueValidateVO
    ┃ ┃ ┣ MappingItemVO
    ┃ ┃ ┣ MappingLineVO
  • aggregate 聚合对象
    一组聚合内聚关系的相关对象的集合.暂时未使用
  • entity 实体对象
    具有唯一标识的对象
  • factory工厂类
    用于构建复杂实体或者聚合对象,隐藏创建细节
  • repository资源库
    对领域的存储和访问进行统一管理的对象,提供查找和持久化对象的方法
  • service领域服务
    实现领域内部对象的行为或操作,处理领域内核心业务逻辑,无法归类于实体对象或值对象的一些操作
  • vo值对象
    无需唯一标识的对象

3.4config层(配置层)

┣ config 
    ┃ ┣ MapSwaggerApiConfig 
    ┃ ┣ MapInitalizingConfig

3.5 infra(基础设施层)

为其他层提供底层依赖

  • 为应用层,传递消息
  • 为领域层,提供持久化机制
  • 为用户展现层,提供组件配置
┣ infra
    ┃ ┣ constant
    ┃ ┃ ┣ ExceptionConstants
    ┃ ┃ ┣ H1BaseConstants
    ┃ ┣ feign
    ┃ ┃ ┣ HzeroLovClient
    ┃ ┣ mapper
    ┃ ┃ ┣ MappingHeaderMapper
    ┃ ┃ ┣ MappingItemMapper
    ┃ ┃ ┣ MappingRuleLineMapper
    ┃ ┃ ┣ MappingSourceLineMapper
    ┃ ┣ repository
    ┃ ┃ ┣ impl
    ┃ ┃ ┃ ┣ MappingHeaderRespositoryImpl
    ┃ ┃ ┃ ┣ MappingItemRepositoryImpl
    ┃ ┃ ┃ ┣ MappingRuleHeaderRespositoryImpl
    ┃ ┃ ┃ ┣ MappingRuleLineRepositoryImpl
    ┃ ┣ util
    ┃ ┃ ┣ DateUtils
    ┃ ┃ ┣ TimeSection
    ┃ ┃ ┣ H1DetailsHelper
  • constant 系统常量
  • feign feign调用
  • mapper mybatis的mapper接口
  • repository.impl 资源库实现类接口
  • util 工具类

实战

4.1 单体定义功能

单表基础数据的定义功能,不需要领域服务,可以直接在展现层调用基础设施层资源库的方法做持久化。以 映射匹配项定义 功能为例:

map  
    ┣ api  
    ┃ ┣ controller
    ┃ ┃ ┣ v1
    ┃ ┃ ┃ ┣ MappingItemController
    ┣ app 
    ┃ ┣ service
    ┃ ┃ ┣ MappingItemService
    ┃ ┃ ┣ impl
    ┃ ┃ ┃ ┣ MappingItemServiceImpl
    ┣ domain 
    ┃ ┣ entity
    ┃ ┃ ┣ MappingItem
    ┃ ┣ repository
    ┃ ┃ ┣ MappingItemRepository
    ┣ infra
    ┃ ┣ constant
    ┃ ┃ ┣ RedisKeyConstants
    ┃ ┣ mapper
    ┃ ┃ ┣ MappingItemMapper
    ┃ ┣ repository
    ┃ ┃ ┣ impl
    ┃ ┃ ┃ ┣ MappingItemRepositoryImpl

resource
    ┣ mapper
    ┃ ┣ MappingItemMapper.xml

4.1.1 api展现层

  • 查询直接使用的资源库进行查询
  • 批量更新和批量删除使用app层的service的方法
public class MappingItemController extends BaseController {

    @Autowired
    private MappingItemRepository mappingItemRepository;
    @Autowired
    private MappingItemService mappingItemService;

    @ApiOperation(value = "映射匹配项表列表")
    @Permission(level = ResourceLevel.ORGANIZATION)
    @GetMapping("/list")
    public ResponseEntity> list(MappingItem mappingItem,
                                                  @ApiIgnore @SortDefault(value = MappingItem.FIELD_MAPPING_ITEM_CODE,
                                    direction = Sort.Direction.ASC) PageRequest pageRequest) {
        Criteria criteria = new Criteria(mappingItem);
        criteria.where(new WhereField(MappingItem.FIELD_MAPPING_ITEM_CODE, Comparison.LIKE),
                        new WhereField(MappingItem.FIELD_DESCRIPTION, Comparison.LIKE),
                        new WhereField(MappingItem.FIELD_SOURCE, Comparison.LIKE));

        Page list = PageHelper.doPageAndSort(pageRequest,
                        () -> mappingItemRepository.selectOptional(mappingItem, criteria));
        return Results.success(list);
    }
    @ApiOperation(value = "批量更新映射匹配项表")
    @Permission(level = ResourceLevel.ORGANIZATION)
    @PostMapping("/batchSave")
    public ResponseEntity> batchSave(@PathVariable Long organizationId,
                                                         @RequestBody List lists) {
        lists.forEach(r -> r.setTenantId(organizationId));
        validObject(lists);
        mappingItemService.batchSave(lists);
        return Results.success(lists);
    }

    @ApiOperation(value = "批量删除映射匹配项表")
    @Permission(level = ResourceLevel.ORGANIZATION)
    @DeleteMapping("/batchDelete")
    public ResponseEntity> batchDelete(@PathVariable Long organizationId,
                                                         @RequestBody List lists) {
        lists.forEach(r -> r.setTenantId(organizationId));
        validObject(lists);
        mappingItemService.batchDelete(lists);
        return Results.success(lists);
    }
    
}

4.1.2 app应用层

  • 进行事务的控制,调用infra层资源库的操作持久化并做缓存的处理
@Service
@Transactional(rollbackFor = Exception.class)
public class MappingItemServiceImpl implements MappingItemService {

    @Autowired
    private MappingItemRepository mappingItemRepository;

    @Override
    public int batchSave(List lists) {
        int saveCount = mappingItemRepository.batchSave(lists);
        for (MappingItem item : lists) {
            if (AuditDomain.RecordStatus.create.equals(item.get_status())) {
                mappingItemRepository.setMappingItemToCache(item);
            } else {
                mappingItemRepository.delMappingItemFromCache(item);
            }
        }
        return saveCount;
    }

    @Override
    public void batchDelete(List lists) {
        mappingItemRepository.batchDelete(lists);
        for (MappingItem item : lists) {
            mappingItemRepository.delMappingItemFromCache(item);
        }
    }

}

4.1.3 domain领域层

  • 数据库表字段
  • 构造redis的key
public class MappingItem extends AuditDomain {
//
    // 业务方法(按public protected private顺序排列)
    // ------------------------------------------------------------------------------
    public static String getCacheKey(Long tenantId, String mappingItemCode) {
        return RedisKeyConstants.MAP_ITEM + tenantId + "_" + mappingItemCode;
    }

    //
    // 数据库字段
    // ------------------------------------------------------------------------------

    @ApiModelProperty("映射项ID")
    @Id
    @GeneratedValue
    @Where
    private Long mappingItemId;
    @ApiModelProperty(value = "映射项代码", required = true)
    @NotBlank
    @Where
    private String mappingItemCode;
    @Where
    @MultiLanguageField
    @ApiModelProperty(value = "描述")
    private String description;
    ...
}

4.1.4 infra基础层

  • 资源库中增加了redis的操作,处理缓存
@Component
public class MappingItemRepositoryImpl extends BaseRepositoryImpl implements MappingItemRepository {

    @Autowired
    private MappingItemMapper mappingItemMapper;

    @Qualifier("redisHelper")
    @Autowired
    private RedisHelper redisHelper;

    public static Logger logger = LoggerFactory.getLogger(MappingItemServiceImpl.class);
    public void setMappingItemToCache(MappingItem item) {
        this.redisHelper.objectSet(MappingItem.getCacheKey(item.getTenantId(), item.getMappingItemCode()), item);
    }

    public void delMappingItemFromCache(MappingItem item) {
        this.redisHelper.delKey(MappingItem.getCacheKey(item.getTenantId(), item.getMappingItemCode()));
    }


}

4.2 复杂业务逻辑功能

  • 领域
    广义上讲,领域(Domain)即是一个组织所做的事件以及其中所包含的一切。每个组织都有它自己的业务范围和做事方式。这个业务范围以及在其中所进行的活动便是领域
  • 子域
    在整个领域中,我们如何对其进行拆分,然后满足我们的业务逻辑
  • 限界上下文
    • 限的意思就是划分、规定,界就是界限、或者一个边界,上下文就是业务流程
    • 限界上下文定义了领域模型的边界,目的是清理子域,然后区分子域哪些是核心域,支撑子域和通用子域
    • 一般一个限界上下文对应一个子域。另外,一个限界上下文有可能包含的不只一个子域

以 映射值配置功能为例:

4.2.1 梳理限界上下文

DDD分层架构_第2张图片

4.2.2 目录结构

map  
    ┣ api
    ┃ ┣ assembler
    ┃ ┃ ┣ Assembler                             #dto装配器
    ┃ ┣ controller
    ┃ ┃ ┣ v1
    ┃ ┃ ┃ ┣ MappingHeaderController             #映射头
    ┃ ┃ ┃ ┣ MappingSourceLineController         #来源行
    ┃ ┃ ┃ ┣ MappingValueRelationController      #关联关系
    ┃ ┃ ┃ ┣ ...
    ┃ ┣ dto
    ┃ ┃ ┣ MappingItemValueDTO                   #映射项值dto
    ┃ ┃ ┣ MappingLineDTO                        #映射行dto(包括来源行dto和目标行dto)
    ┃ ┃ ┣ MappingSourceLineDTO                  #来源行dto
    ┃ ┃ ┣ MappingTargetLineDTO                  #目标行dto
    ┃ ┃ ┣ MappingValueLineDTO                   #映射值行dto(包括映射项值dto)
    ┃ ┃ ┃ ...
    ┣ app 
    ┃ ┣ service
    ┃ ┃ ┣ MappingLineService                    #映射行服务
    ┃ ┃ ┣ MappingValueLineService               #映射行值服务
    ┃ ┃ ┣ ...
    ┃ ┃ ┣ impl
    ┃ ┃ ┃ ┣ MappingHeaderServiceImpl
    ┃ ┃ ┃ ┣ ...
    ┣ domain 
    ┃ ┣ entity
    ┃ ┃ ┣ MappingHeader                         #映射头实体
    ┃ ┃ ┣ MappingSourceLine                     #来源行实体
    ┃ ┃ ┣ MappingTargetLine                     #目标行实体
    ┃ ┃ ┣ MappingValueRelation                  #映射关联实体
    ┃ ┃ ┣ ...
    ┃ ┣ vo
    ┃ ┃ ┣ MappingItemValueValidateVO            #映射项值校验vo
    ┃ ┃ ┣ MappingLineVO                         #映射行vo
    ┃ ┣ service
    ┃ ┃ ┣ MappingDomainService                  #映射领域服务
    ┃ ┃ ┣ MappingLineDomainService              #映射行领域服务
    ┃ ┃ ┣ MappingValueDomainService             #映射行值领域服务
    ┃ ┃ ┣ impl
    ┃ ┃ ┃ ┣ MappingDomainServiceImpl
    ┃ ┃ ┃ ┣ ...
    ┃ ┣ repository
    ┃ ┃ ┣ MappingValueRelationRepository
    ┃ ┃ ┣ ...
    ┣ infra
    ┃ ┣ constant
    ┃ ┃ ┣ RedisKeyConstants
    ┃ ┣ mapper
    ┃ ┃ ┣ MappingHeaderMapper
    ┃ ┃ ┣ ...
    ┃ ┣ repository
    ┃ ┃ ┣ impl
    ┃ ┃ ┃ ┣ MappingHeaderRepositoryImpl
    ┃ ┃ ┃ ┣ ...
    ┃ ┣ util
    ┃ ┃ ┣ DateUtils                             #日期比较工具
    ┃ ┃ ┣ TimeSection                           #时间区间工具
resource
    ┣ mapper
    ┃ ┣ MappingValueRelationMapper.xml
    ┃ ┣ ...
    

4.2.3 api展现层

  • 映射配置行页面进行初始化查询,动态加载列。
  • 映射配置行的查询,保存和删除
public class MappingValueLineController {

    @Autowired
    MappingLineService mappingLineService;

    @Autowired
    MappingValueLineService mappingValueLineService;


    /**
     * 映射行-初始化查询,将来源和目标封装返回
     *
     * @param mappingHeaderId 映射头id
     * @return
     * @author quyuanyuan 2020-05-21 9:54
     */
    @GetMapping(value = "/init")
    @Permission(level = ResourceLevel.ORGANIZATION)
    @ApiOperation(value = "映射配置行进页面初始化查询")
    public ResponseEntity init(
                    @RequestParam @ApiParam(value = "映射头id", required = true) Long mappingHeaderId) {
        MappingLineDTO mappingValueLineInitDTO = mappingLineService.getInitLineByHeaderId(mappingHeaderId);
        return Results.success(mappingValueLineInitDTO);
    }

    /**
     * 映射配置值查询
     *
     * @param queryValueDTO 映射头id
     * @return
     * @author quyuanyuan 2020-05-21 9:54
     */
    @PostMapping(value = "/query")
    @Permission(level = ResourceLevel.ORGANIZATION)
    @ApiOperation(value = "映射配置值查询")
    public ResponseEntity> query(
                    @RequestBody @ApiParam(value = "映射值查询条件") MappingValueLineDTO queryValueDTO,
                    @PathVariable @ApiParam(value = "租户id", required = true) Long organizationId,
                    PageRequest pageRequest) {
        Page lists = mappingValueLineService.query(queryValueDTO, organizationId, pageRequest);
        return Results.success(lists);
    }

    /**
     * 映射配置值保存
     *
     * @param list 值
     * @param organizationId 租户id
     * @return
     * @author quyuanyuan 2020-05-21 19:50
     */
    @PostMapping(value = "/batchSave")
    @Permission(level = ResourceLevel.ORGANIZATION)
    @ApiOperation(value = "映射配置行保存")
    public ResponseEntity> batchSave(
                    @RequestBody @ApiParam(value = "映射值行列表", required = true) List list,
                    @PathVariable @ApiParam(value = "租户id", required = true) Long organizationId) {
        mappingValueLineService.batchSave(list, organizationId);
        return Results.success(list);
    }

    /**
     * 映射配置值删除
     *
     * @param list 值
     * @return
     * @author quyuanyuan 2020-05-22 19:50
     */
    @PostMapping(value = "/batchDelete")
    @Permission(level = ResourceLevel.ORGANIZATION)
    @ApiOperation(value = "映射配置行批量删除")
    public ResponseEntity batchDelete(
                    @RequestBody @ApiParam(value = "映射值行列表", required = true) List list,
                    @PathVariable @ApiParam(value = "租户id", required = true) Long organizationId) {
        mappingValueLineService.batchDelete(list, organizationId);
        return Results.success();
    }



}

4.2.4 app应用层

映射行服务

public interface MappingLineService {

    /**
     * 根据映射头获取来源和目标列,用于页面字段
     *
     * @param mappingHeaderId 映射头id
     * @author quyuanyuan 2020-05-19 9:23
     * @return io.choerodon.core.domain.Page
     */
    MappingLineDTO getInitLineByHeaderId(Long mappingHeaderId);

}

映射值行服务

public interface MappingValueLineService {

    /**
     * 映射关联值保存
     *
     * @param list
     * @param tenantId
     * @author quyuanyuan 2020-05-21 15:10
     * @return java.util.List
     */
    List batchSave(List list, Long tenantId);

    /**
     * 映射关联值查询
     *
     * @param queryValueDTO
     * @param pageRequest
     * @author quyuanyuan 2020-05-21 15:10
     * @return List
     */
    Page query(MappingValueLineDTO queryValueDTO, Long tenantId, PageRequest pageRequest);

    /**
     * 映射关联值批量删除
     *
     * @param list
     * @author quyuanyuan 2020-05-22 15:10
     * @return
     */
    void batchDelete(List list, Long tenantId);
}

4.2.5 domain领域层

  • 映射值领域服务,处理映射值关联关系的新增和更新
  • 映射来源行值与目标行值的操作使用单体的资源库
  • 校验默认值在时间区间中是否存在交叉
@Service
public class MappingValueDomainServiceImpl implements MappingValueDomainService {

    /**
     *  新增关联关系
     *
     * @param mappingValueRelation
     * @author quyuanyuan 2020-06-22 11:24
     * @return com.hand.hfins.map.domain.entity.MappingValueRelation
     */
    @Override
    public MappingValueRelation insertRelation(MappingValueRelation mappingValueRelation) {
        ...
    }

    /**
     *  更新关联关系
     *
     * @param mappingValueRelation 新的记录
     * @param oldMappingValueRelation 历史的记录
     * @author quyuanyuan 2020-06-22 11:24
     * @return com.hand.hfins.map.domain.entity.MappingValueRelation
     */
    @Override
    public MappingValueRelation updateRelation(MappingValueRelation mappingValueRelation,
                    MappingValueRelation oldMappingValueRelation) {
        ...
    }
    
    /**
     * 
     * 校验维护映射的默认值,所有的默认值中,时间区间存在重叠,则报错
     * 
     * @param mappingHeaderId 映射头ID
     * @throws io.choerodon.core.exception.CommonException exception
     * @author quyuanyuan 2021/5/27 16:20
     **/
    private void checkMappingDefaultValue(Long filterRelationId,Long mappingHeaderId, List sourceValueLines,
                    Date startDate, Date endDate) {
        ...
    }

}

你可能感兴趣的:(领域驱动设计DDD)