DDD之Repository模式

一,传统架构下的实体模型

传统的应用架构,几乎就是根据需求设计数据库的表,根据表建立实体,对应着实体的就是DAO,Service,Controller,也就是传统的MVC三层架构。

回顾下我们平时写的代码,里面有着很多的xxxUtils工具类,很多的参数校验逻辑与业务逻辑混杂在一起,很多的实体类直接与数据库进行一对一映射。

好处很明显,在业务初期,开发起来很容易,相对比较简单,流水线式编码,但是,一旦后期需求变更,业务改造,数据库表发生变化,可能给我们带来毁灭性的负担。所谓牵一发而动全身,前面欠下了技术债,后面很难受,想补救工作量巨大,不补救,系统难以升级,难以扩展,灵活性急剧下降。

对于第三方(包括但不限于数据库)的强依赖,导致我们在做业务扩展的时候,顾虑重重,缺少了一往无前的动力。

但是截至目前,包括我所参与开发的项目,依然是采用这种模式,为什么?

  • 数据库思维:从有了数据库的那一天起,开发人员的思考方式就逐渐从“写业务逻辑“转变为了”写数据库逻辑”,也就是我们经常说的在写CRUD代码。
  • 所谓简单:贫血模型的优势在于“简单”,仅仅是对数据库表的字段映射,所以可以从前到后用统一格式串通。这里简单打了引号,是因为它只是表面上的简单,实际上当未来有模型变更时,你会发现其实并不简单,每次变更都是非常复杂的事情。
  • 脚本思维:很多常见的代码都属于“脚本”或“胶水代码”,也就是流程式代码。脚本代码的好处就是比较容易理解,但长久来看缺乏健壮性,维护成本会越来越高。

两个概念,你是否明确?

  • 数据模型:也就是和数据库一一映射的类
  • 业务模型/领域模型:业务逻辑中,相关连的数据如何联动

真实代码结构中,Data Model和 Domain Model实际上会分别在不同的层里,Data Model只存在于数据层,而Domain Model在领域层,而链接了这两层的关键对象,就是Repository。

二,Repository的作用

在传统的MVC三层架构中,我们操作数据库的层,一般叫做DAO,或者Mapper层。

由于他与数据库直接耦合,导致了强依赖性。更可怕的是,由于我们都是在Service层直接注入Mapper层,导致了这种强依赖的传递,也就是整个应用体系开始变得越加依赖数据库DB。

举一个例子

public Interface UserDao{
   
    public List<User> selectUserByIds(List<Integer> ids);
}

public class UserService{
   
    
    @Resource
    private UserDao userDao;

    public List<User> getUserList(List<Integer> ids){
   
        return userDao.selectUserByIds(ids);
    }
    
}

这个代码,咋一看,简单明了,没有任何问题。但是假如现在由于数据量的增长和访问数量的增加,我需要引入缓存的逻辑,假如有十个地方调用了这个DAO中的方法,我需要在这十个地方都修改成:

public class UserService{
   
    
    @Resource
    private UserDao userDao;
    @Resource
    private RedisTemplate redisTemplate;

    public List<User> getUserList(List<Integer> ids){
   
        List<User> users=redisTemplate.opsForValue().get(key);
        if(users!=null){
   
            return users;
        }else{
   
            List<User> userList=userDao.selectUserByIds(ids);
            redisTemplate.opsForValue().set(key,userList);
            return userList;
        }
        
    }

}

所以,需要一个逻辑,能够隔离业务逻辑与DB之间的传递强耦合关系,让我们的应用更加灵活,健壮,这个就是Repository的价值。

三,模型对象代码规范

1.什么是DO,DTO,Entity?

  • Data Object:DO的字段类型和名称应该和数据库物理表格的字段类型和名称一一对应,这样我们不需要去跑到数据库上去查一个字段的类型和名称。
  • Entity:实体对象是我们正常业务应该用的业务模型,它的字段和方法应该和业务语言保持一致,和持久化方式无关。也就是说,Entity和DO很可能有着完全不一样的字段命名和字段类型,甚至嵌套关系。Entity的生命周期应该仅存在于内存中,不需要可序列化和可持久化。
  • DTO:主要作为Application层的入参和出参,比如CQRS里的Command、Query、Event,以及Request、Response等都属于DTO的范畴。DTO的价值在于适配不同的业务场景的入参和出参,避免让业务对象变成一个万能大对象。

2.对象之间的关系

在实际开发中DO、Entity和DTO不一定是1:1:1的关系。一些常见的非1:1关系如下:

复杂的实体拆分成多张数据库的表:常见的原因,字段多,查询性能差,需要将非检索、大字段等单独存为一张表,提升基础信息表的检索效率。

当然,除了一些数据查询频繁,聚合性非常强的表。

拆分的实体:我接触过的,订单,商品,购物车。

3.模型所在模块和转化器

由于现在从一个对象变为3+个对象,对象间需要通过转化器(Converter/Mapper)来互相转化。而这三种对象在代码中所在的位置也不一样,简单总结如下:

在这里插入图片描述

DTO Assembler:在Application层,Entity到DTO的转化器有一个标准的名称叫DTO Assembler。Martin Fowler在P of EAA一书里对于DTO 和 Assembler的描述:Data Transfer Object。DTO Assembler的核心作用就是将1个或多个相

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值