DDD系列-3

DDD系列-3为什么要用 Repository 实体模型与贫血模型 Entiry 实体 ER 模型 用来描述实体之间的关系 而后演变为一个数据模型 在关系数据库中代表了数据存储的方式 许多 ORM 框架 忽略了 Entiry 本身行为 导致许多模型仅包含了实体数据 属性 而实体的业务逻辑被分散在多个服务

大家好,我是讯享网,很高兴认识大家。

为什么要用Repository

实体模型与贫血模型

Entiry(实体)-ER模型:用来描述实体之间的关系,而后演变为一个数据模型,在关系数据库中代表了数据存储的方式。

许多ORM框架,忽略了Entiry本身行为,导致许多模型仅包含了实体数据(属性),而实体的业务逻辑被分散在多个服务、Controller、Utils工具类中–贫血模型

贫血模型特征
  • 大量的XxxDo对象:是数据库表结构的映射,里面没有包含(或包含了很少的)业务逻辑
  • 服务和Controller里有大量的业务逻辑:校验逻辑、计算逻辑、格式转化逻辑、对象关系逻辑、数据存储逻辑等;
  • 大量的Utils工具类
缺点
  • 无法保护模型对象的完整性和一致性
  • 象操作的可发现性极差
  • 代码逻辑重复
原因
  • 数据库思维:开发人员的思考方式就逐渐从“写业务逻辑“转变为了”写数据库逻辑”,也就是我们经常说的在写CRUD代码
  • 贫血模型“简单”:仅仅是对数据库表的字段映射
  • 脚本思维
核心原因
  • 数据模型(Data Model):指业务数据该如何持久化,以及数据之间的关系,也就是传统的ER模型;----只存在于数据层
  • 业务模型/领域模型(Domain Model):指业务逻辑中,相关联的数据该如何联动。----领域层

模型对象代码规范

1. 对象类型

1.1 Data Object(DO、数据对象)

DDD的规范里,DO应该仅仅作为数据库物理表格的映射,不能参与到业务逻辑中。

1.2 Entity(实体对象)

是我们正常业务应该用的业务模型,它的字段和方法应该和业务语言保持一致,和持久化方式无关。

1.3 DTO(传输对象)

主要作为Application层的入参和出参,比如CQRS里的Command、Query、Event,以及Request、Response等都属于DTO的范畴。DTO的价值在于适配不同的业务场景的入参和出参.


讯享网

2.模型对象之间的关系

Do、Entiry、DTO不一定是1:1:1关系:

  • 复杂的Entity拆分多张数据库表:常见的原因在于字段过多,导致查询性能降低,需要将非检索、大字段等单独存为一张表,提升基础信息表的检索效率。
  • 多个关联的Entity合并一张数据库表:这种情况通常出现在拥有复杂的Aggregate Root - Entity关系的情况下,且需要分库分表,为了避免多次查询和分库分表带来的不一致性,牺牲了单表的简洁性,提升查询和插入性能。
  • 从复杂Entity里抽取部分信息形成多个DTO:这种情况通常在Entity复杂,但是调用方只需要部分核心信息的情况下,通过一个小的DTO降低信息传输成本。
  • 合并多个Entity为一个DTO:这种情况通常为了降低网络传输成本,降低服务端请求次数,将多个Entity、DP等对象合并序列化,并且让DTO可以嵌套其他DTO。

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

在这里插入图片描述

3.1 DTO Assembler
  • 在Application层(可变)
  • 核心作用就是将1个多个相关联的Entity转化为1个或多个DTO
3.2 Data Converter
  • 在Infrastructure层(固件不易变)
  • Entity到DO的转化器没有一个标准名称,但是为了区分Data Mapper,我们叫这种转化器Data Converter。

4.Repository代码规范

传统Data Mapper(DAO)属于“固件”,和底层实现(DB、Cache、文件系统等)强绑定,如果直接使用会导致代码“固化”。所以为了在Repository的设计上体现出“软件”的特性,主要需要注意以下三点:

  • 接口名称不应该使用底层实现的语法: 我们常见的insert、select、update、delete都属于SQL语法,使用这几个词相当于和DB底层实现做了绑定。相反,我们应该把 Repository 当成一个中性的类似Collection 的接口,使用语法如 find、save、remove。
  • 出参入参不应该使用底层数据格式:Repository 操作的是 Entity 对象(实际上应该是Aggregate Root),而不应该直接操作底层的 DO 。更近一步,Repository 接口实际上应该存在于Domain层,根本看不到 DO 的实现。
  • 应该避免所谓的“通用”Repository模式
 // 代码在Infrastructure层 @Repository // Spring的注解 public class OrderRepositoryImpl implements OrderRepository { 
    private final OrderDAO dao; // 具体的DAO接口 private final OrderDataConverter converter; // 转化器 public OrderRepositoryImpl(OrderDAO dao) { 
    this.dao = dao; this.converter = OrderDataConverter.INSTANCE; } @Override public Order find(OrderId orderId) { 
    OrderDO orderDO = dao.findById(orderId.getValue()); return converter.fromData(orderDO); } @Override public void remove(Order aggregate) { 
    OrderDO orderDO = converter.toData(aggregate); dao.delete(orderDO); } @Override public void save(Order aggregate) { 
    if (aggregate.getId() != null && aggregate.getId().getValue() > 0) { 
    // update OrderDO orderDO = converter.toData(aggregate); dao.update(orderDO); } else { 
    // insert OrderDO orderDO = converter.toData(aggregate); dao.insert(orderDO); aggregate.setId(converter.fromData(orderDO).getId()); } } @Override public Page<Order> query(OrderQuery query) { 
    List<OrderDO> orderDOS = dao.queryPaged(query); long count = dao.count(query); List<Order> result = orderDOS.stream().map(converter::fromData).collect(Collectors.toList()); return Page.with(result, query, count); } @Override public Order findInStore(OrderId id, StoreId storeId) { 
    OrderDO orderDO = dao.findInStore(id.getValue(), storeId.getValue()); return converter.fromData(orderDO); } } 

讯享网
小讯
上一篇 2025-01-10 10:07
下一篇 2025-02-08 09:24

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/59246.html