DDD领域驱动设计项目落地

 背景

        本文也只是本人对DDD和Cola 4.0架构的浅显理解,在这个理解基础上将一个简单的用户权限体系整改为符合领域驱动设计的一个项目结构,有不对的地方请广大读者多多包容并指正。

下图是cola4.0的架构,我的实现更倾向这种架构。

业务场景分析

        用户权限体系属于一个比较简单的系统,主要分为 用户、角色、菜单、机构、鉴权等这几个主要功能模块,其中还涉及用户-角色、角色-菜单、用户-机构等关系,此时就要考虑如何划分领域,一开始,我是打算直接一个用户领域搞定,主要是因为用户好像和其他几个模块都有交集,但是仔细思考,其实可以分的更细,让各业务的边界更加清晰,看下图。

各子域情况

1. 机构子域 :只处理机构业务的逻辑,不和其他子域产生耦合,在应用层处理组合逻辑

2. 用户子域 :只处理用户业务的逻辑,不和其他子域产生耦合,在应用层处理组合逻辑

3. 角色子域:只处理角色业务的逻辑,不和其他子域产生耦合,在应用层处理组合逻辑

4. 菜单子域:只处理菜单业务的逻辑,不和其他子域产生耦合,在应用层处理组合逻辑

5. 用户-机构子域 : 这个子域就会在其聚合根中维护用户值对象和机构值对象,相当于关联关系只存在于这里,具体看下面代码,会发现,在用户-机构子域中的repository中会引用用户和机构的repository ,虽然领域之间最好不要耦合,但是基于这种情况,也没办法,不过repository 是接口,已经在一定程度上降低了领域之间的耦合。

@Data
public class UserOrgDO {

    private Long id;

    private Long orgId;

    private Long userId;

    private OrgItem orgItem;

    private UserItem userItem;

    /**
     * 判断是否被使用
     *
     * @return true: 被使用,false : 没被使用
     */
    public boolean checkIsUsed() {
        return userItem != null;
    }

}


@Component
@RequiredArgsConstructor
public class UserOrgRepositoryImpl implements UserOrgRepository {

    private final SysUserOrgMapper sysUserOrgMapper;

    private final UserRepository userRepository;

    private final OrgRepository orgRepository;

    @Override
    public List<UserOrgDO> queryByOrgIds(Long[] ids) {
        CommonExceptionCode.PARAMS_ERROR
                .assertNotNull(ids, "机构id不能为null")
                .assertFalse(ids.length == 0, "机构id不能为空");
        List<SysUserOrg> userOrgs = sysUserOrgMapper.selectList(Wrappers.<SysUserOrg>lambdaQuery().in(SysUserOrg::getOrgId, ids));
        return convertToDO(userOrgs);
    }

    @Override
    public List<UserOrgDO> queryByUserIds(Long[] ids) {
        CommonExceptionCode.PARAMS_ERROR
                .assertNotNull(ids, "机构id不能为null")
                .assertFalse(ids.length == 0, "机构id不能为空");
        List<SysUserOrg> userOrgs = sysUserOrgMapper.selectList(Wrappers.<SysUserOrg>lambdaQuery().in(SysUserOrg::getUserId, ids));
        return convertToDO(userOrgs);
    }

    private List<UserOrgDO> convertToDO(List<SysUserOrg> userOrgs) {
        return userOrgs.stream().map(x -> {
            UserOrgDO userOrgDO = new UserOrgDO();
            userOrgDO.setOrgId(x.getOrgId());
            userOrgDO.setUserId(x.getUserId());
            Optional<UserItem> userItemOptional = userRepository.queryById(x.getUserId());
            userItemOptional.ifPresent(userOrgDO::setUserItem);
            Optional<OrgItem> orgItemOptional = orgRepository.queryById(x.getOrgId());
            orgItemOptional.ifPresent(userOrgDO::setOrgItem);
            return userOrgDO;
        }).collect(Collectors.toList());
    }
}

6. 用户-角色子域:同上

7. 角色-菜单子域: 同上

项目结构

- adapter
    -- controller  # controller的web接口
        UserController
        MenuController
        RoleController
        OrgController
-- app
    -- dto # 数据传输对象 
    -- converter  # 实现dto和do的转换 
    -- service  # 应用层,负责领域服务的组合
        UserService
        MenuService
        RoleService
        OrgService
-- domain
    -- org  # 组织机构子域
        -- exception # 业务异常
        -- model
        -- repository
            -- impl
                OrgRepositoryImpl
            OrgRepository
        -- service
            OrgDomainService
    -- menu   # 菜单子域
        -- model
        -- repository
            -- impl
                MenuRepositoryImpl
            MenuRepository
        -- service
            MenuDomainService
    -- role   # 角色子域
        -- model    
        -- repository
            -- impl
                RoleRepositoryImpl
            RoleRepository
        -- service
            RoleDomainService
    -- user # 用户子域
        -- model
        -- enums
        -- repository
            -- impl
                UserRepositoryImpl
            UserRepository
        -- service
           UserDomainService
    -- userOrg  # 用户和组织机构的关系子域
        -- model
           UserOrgDO
        -- repository
            -- impl
                UserOrgRepositoryImpl
            UserOrgRepository
        -- service
            UserOrgDomainService
    -- roleMenu  # 角色和菜单的关系子域
        -- model
           RoleMenuDO
        -- repository
            -- impl
                RoleMenuRepositoryImpl
            RoleMenuRepository
        -- service
            RoleMenuDomainService
    -- userRole  # 用户和角色的关系子域
        -- model
           UserRoleDO
        -- repository
            -- impl
                UserRoleRepositoryImpl
            UserRoleRepository
        -- service
            UserRoleDomainService
-- infrastructure
    -- config  # 配置文件
    -- mapper  # 数据库操作(持久化映射)
    -- entity  # 数据库实体(与领域实体映射)
    -- utils  # 工具类
    -- exception  # 异常处理
    -- vo  # 请求和响应对象
    -- constant  # 常量
    -- filter  # 过滤器

项目代码

        假如我写的没问题的话,我再贴代码地址吧。

心得

1. 域之间最好是不要产生依赖,若是非要产生,请用接口 或者 抽象类
2. 域中不能使用DTO,只能用DO
3. 基础设施层可以作为领域层对外访问的中介,领域层提供接口,防腐层实现去处理外部接口
4. 业务域可以使用通用域
5. 业务域中没有领域实体也可以
6. 应用层可以调用 基础设施层、领域层
7. 领域实体拥有唯一标识,聚合的概念是为了保证数据的一致性,充血模型,包含一些方法
8. 值对象只有构造方法和getter方法,往往是一组不可拆分的属性
9. 聚合就是业务相关的属性和值对象的组合,入口就是聚合根
10. VO在api层转化为DTO,然后传递给应用层,应用层将DTO转化成为DO,传递给领域层,领域层将DO转化为PO,进行数据库操作
11. 枚举类的位置,取决于是通用的还是指定业务的,指定业务的,就放在业务领域中,通用的就放在通用域中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值