服务端模块化架构设计 2.0|结合DDD与MVC的中庸之道(优化与插件)

2023年 8月 21日 42.9k 0

本专栏 将通过以下几块内容来搭建一个 模块化:可以根据项目的功能需求和体量进行任意模块的组合或扩展 的后端服务

项目结构与模块化构建思路

RESTful与API设计&管理

网关路由模块化支持与条件配置

DDD领域驱动设计与业务模块化(概念与理解)

DDD领域驱动设计与业务模块化(落地与实现)

DDD领域驱动设计与业务模块化(薛定谔模型)

DDD领域驱动设计与业务模块化(优化与重构)

RPC模块化设计与分布式事务

v2.0:项目结构优化升级

v2.0:项目构建+代码生成「插件篇」

v2.0:扩展模块实现技术解耦

v2.0:结合DDD与MVC的中庸之道(启发与思路)

v2.0:结合DDD与MVC的中庸之道(标准与实现)

v2.0:结合DDD与MVC的中庸之道(优化与插件)(本文)

未完待续......

在之前的文章 v2.0:项目结构优化升级 中,我们将项目的结构进行了优化,优化之后的项目结构如下

项目结构v2.png

简单说明

考虑到可能有读者是第一次看这个专栏,所以我还是先简单介绍一下,详细的内容大家可以看我之前的专栏文章

这里模块化的设想其实就是可插拔的功能模块,如果需要这个功能,就把这个模块用GradleMaven的方式编译进来,如果不需要,去掉对应的依赖就行了,避免改动代码,因为一旦涉及到代码改动的话,就会变得“改不断,理还乱”

当然了,需要达到每个模块都能够任意拆卸的程度其实并不简单,所以我借鉴了DDD的思想来达成这个目的,专栏中也有这一块的内容,今天这篇文章就是对DDD的进一步优化

当我们把所有模块都编译进来的时候,那么就是一个单体应用,如果把多个模块分开编译,那么就变成了微服务

以我们juejin项目的三个模块(用户,沸点,通知)为例:

在最开始的时候,我们可以将这三个模块打包成一个服务,作为单体应用来运行,适合项目前期体量较小的情况

之后,当我们发现沸点的流量越来越大,就可以将沸点模块拆分出来作为单独的一个服务方便扩容,用户模块和通知模块打包在一起作为另一个服务,这样就从单体应用变成了微服务

注:从单体应用到微服务的切换是不需要修改代码的

领域模型嵌套查询

在上一篇文章结合DDD与MVC的中庸之道(标准与实现)中我们的用户存储是这样实现的

//基于 MBP 的用户存储实现
//UserPO 为数据库表对应的数据模型
@Repository
public class MBPUserRepository extends MBPDomainRepository implements UserRepository {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private DomainValidator validator;

    @Override
    public UserPO do2po(User user) {
        UserPO po = new UserPO();
        po.setId(user.getId());
        po.setUsername(user.getUsername());
        po.setPassword(user.getPassword());
        po.setNickname(user.getNickname());
        po.setCreateTime(user.getCreateTime());
        return po;
    }

    @Override
    public User po2do(UserPO po) {
        return new UserImpl.Builder()
                .id(po.getId())
                .username(po.getUsername())
                .password(po.getPassword())
                .nickname(po.getNickname())
                .createTime(po.getCreateTime())
                .build(validator);
    }

    @Override
    public BaseMapper getBaseMapper() {
        return userMapper;
    }
}

对于简单的模型来说没什么问题

我们只要将查询出来的数据转为领域模型即可

嵌套领域对象,单个查询

但是当出现下面这样的模型时

//示例
public interface Sample extends DomainEntity {

    String getSample();

    User getUser();
}

领域模型中还持有了其他的领域模型

很明显是和其他的表产生了关联

而我们的t_sample表可能是这样的结构

id sample user_id
sample1 sample1 user1
sample2 sample2 user2

这个时候可以这样实现用户存储

//基于 MBP 的示例存储实现
@Repository
public class MBPSampleRepository extends MBPDomainRepository implements SampleRepository {

    @Autowired
    private SampleMapper sampleMapper;

    @Autowired
    private DomainFactory factory;

    @Autowired
    private DomainValidator validator;

    @Override
    public SamplePO do2po(Sample sample) {
        SamplePO po = new SamplePO();
        po.setId(sample.getId());
        po.setUserId(sample.getUser().getId());
        return po;
    }

    @Override
    public Sample po2do(SamplePO po) {
        //根据 userId 生成一个 User,该方法不会立即查询数据库,而会等到调用 User 的方法获取数据时才会触发查询
        User user = factory.createObject(User.class, po.getUserId());
        return new SampleImpl.Builder()
                .id(po.getId())
                .sample(po.getSample())
                .user(user)
                .users(users)
                .build(validator);
    }

    @Override
    public BaseMapper getBaseMapper() {
        return sampleMapper;
    }
}

通过DomainFactory生成一个User对象(实际上就是使用了动态代理暂时占了个位置)

只有当调用用户数据的时候才会触发查询(通过传入的id查询数据)

嵌套领域对象,批量查询

查询列表的时候就又出现了一个问题

我们之前的AbstractDomainRepository是这样实现的

//领域存储抽象类
//P 为数据模型
public abstract class AbstractDomainRepository implements DomainRepository {

//数据模型转领域模型
public abstract T po2do(P po);

//数据模型转领域模型
public Collection pos2dos(Collection

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论