DTO

DTO层的思考

 

注意,【】中是后来加的批注。因为随着对DDD的深入了解,对DTO的思考也有所改变。

分布式模式下,DTO层是一定需要的吗?

DTO层的作用是为了隔离Domain Model:让DoMain Model的改动不会直接影响到UI;保持Domain Model的安全,不暴露业务逻辑。

 【最大多数情况看来,UI或者DO的改动,都不可避免地会影响对方,即使中间有DTO隔离,所以这一个理由是不成立的。
至于安全问题,如果不是开放性平台(业务接口的调用者不是安全的),那么也没必要担心业务逻辑会暴露;即使是开放性平台,我们也可以用AOP做权限检查,限制第三者的非法调用。所以安全问题的理由也不成立。】

有两个方案可以省略DTO层,又能起到DTO的作用:

l          继承:定义失血模型的Model,然后再做一个从Model继承的代理类 ,代理类里实现业务逻辑。贫血模型的Model单独为一个DLL,代理模型另起一个DLL。Client端只能引用贫血模型的DLL,这样就达到了隔离的目的,又省略了Contract层。

l          接口:为Domain Model做一个贫血模型的接口,接口单独为一个DLL,Client端只引用接口DLL。

这两种方案的核心思想都是让数据字段与业务方法分离,然后只对Client端公开数据部份。但这种思想会导致域模型趋向事务脚本模型,所以都不可取。

 

综上所述,在使用领域模型的情况下,如果没有DTO层,那么Model是一定会完整地暴露给Client端。

 

暴露Domain Model会带来什么问题?

首先是安全问题。

DoMain Model都带有业务方法,让Client端引用Domain Model就意味着Client端可以绕过Service层直接完成业务逻辑的调用。

 【Client端直接调用Domian Model的方法,甚至直接调用Repository做持久化,在DDD的解决方案中是允许的。至于安全性问题已经在上面反驳过了。】

其次是效率问题。

Domain Model通常很“厚”(Model与Model嵌套得很深),在广域网上传输大对象会有严重的效率问题。

 

再有就是跨平台的问题。

Domain Model都是与特定的语言的数据类型有关,而这些数据类型是不能跨平台的,比如Java的类型就不能被C#使用。但在分布式模式下,Client端与Server端的平台不同是很正常的,如果Service直接返回Domain Model,Client端根本无法解析,这就要求Service返回的结果必须是标准的格式字节流。

让Domain Model只使用简单类型(字符和数值)?

让数据类型约束Domain Model显然不是一个好想法,所以DTO似乎是必不可少的了。

 【是的,跨平台是DTO唯一存在的价值,JSON和XML大行其道并不是没有道理的】

如果我们一定要抛弃DTO层呢?

我们花这么大力气想省略DTO,就是因为这玩意太麻烦了,而且它的作用如此之小,代价却如此之大。

 

如果我们的系统只运行在局域网,网络都是光纤,我们有着强大的服务器,而且我们的开发人员严格地遵守Domain Model调用规则,只使用数据字段,不调用业务方法,并且我们的系统使用同一种语言开发,绝对不会跨平台……

 

我们把一切不利于暴露Domain Model的弱点都屏蔽之后,是不是意味着我们可以省略DTO层了?

嗯,看起来似乎是可以省略了。但前台开发人员真的能严格遵守调用规则吗?估计这时候,前台开发人员会进而质疑Service层的存在意义了……

 【是的,在DDD中Client端是允许绕过Service,直接访问Domain Model的业务方法,以及Repository层,所以简单模块(只有crud,不用跨领域模块的功能)根本没有Service类。】

贫血模型 + 事务脚本模式

贫血模型和事务脚本模式都是与领域模型对立的,领域模型主张充血模型。

 

便贫血模型+事务脚本模式也有好处:

1.          开发简单。大多数的开发人员都有用过这种模式——以PetShop为代表的ADO.NET分层构架(MOD,DAL,BL)。

2.          可以安全地暴露业务模型,不用担心业务方法被非法调用。

在Spring Boot中,DTO(Data Transfer Object)是一种用于数据传输的对象,通常用于在不同层之间传递数据。DTO对象用于封装多个字段或属性,以便将数据从数据库或其他服务层传递到控制器层或前端视图层。 创建DTO对象可以避免直接暴露实体类(Entity)给外部,增加了灵活性和安全性。DTO对象通常只包含需要传输的数据字段,而不包含业务逻辑或复杂的方法。 下面是一个简单的DTO示例: ```java public class UserDTO { private Long id; private String name; private String email; // 其他可能的字段和getter/setter方法 // ... } ``` 在上面的示例中,`UserDTO`是一个简单的DTO类,包含了`id`、`name`和`email`字段。可以根据需要添加其他字段,并提供相应的getter和setter方法。 在实际使用中,可以在服务层或持久层将实体类转换为DTO对象,然后将DTO对象传递给控制器层或前端视图层。这样可以确保数据的安全性和封装性,并且可以根据需要对数据进行转换和处理。 例如,在控制器层处理HTTP请求时,可以接收DTO对象作为方法参数,或者将DTO对象作为方法的返回值,以便进行数据传输。 ```java @RestController @RequestMapping("/api") public class UserController { @Autowired private UserService userService; @PostMapping("/user") public UserDTO createUser(@RequestBody UserDTO userDTO) { // 将用户DTO转换为实体类对象,并调用服务层的方法进行处理 User user = new User(userDTO.getName(), userDTO.getEmail()); User savedUser = userService.createUser(user); // 将保存后的用户实体类对象转换为DTO对象并返回 return new UserDTO(savedUser.getId(), savedUser.getName(), savedUser.getEmail()); } // 其他方法省略... } ``` 在上面的示例中,`createUser()`方法接收一个`UserDTO`对象作为请求体,并将其转换为实体类对象进行处理。然后,将保存后的实体类对象再次转换为DTO对象并返回。 这样,通过使用DTO对象,可以在不同层之间进行数据传输,并进行必要的转换和处理,以实现更好的数据封装和安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值