【发布时间】:2020-08-01 19:41:51
【问题描述】:
我正在遵循控制器、服务和存储库模式,我只是想知道 DTO 是从哪里来的。
控制器是否应该只接收 DTO?我的理解是你不希望外界知道底层域模型?
从域模型到 DTO 的转换应该发生在控制器层还是服务层?
【问题讨论】:
标签: java spring spring-boot dto
我正在遵循控制器、服务和存储库模式,我只是想知道 DTO 是从哪里来的。
控制器是否应该只接收 DTO?我的理解是你不希望外界知道底层域模型?
从域模型到 DTO 的转换应该发生在控制器层还是服务层?
【问题讨论】:
标签: java spring spring-boot dto
在今天使用 Spring MVC 和交互式 UI 进行编程时,Web 应用程序实际上有 4 层:
UI 层(Web 浏览器、JavaScript)
MVC 控制器,即带有 @Controller 注释的 Spring 组件
服务层,即用@Service注解的Spring组件
数据访问层,即用@Repository注解的Spring组件
每次这些层与底层交互时,它们都需要发送/接收数据,通常是 POJO,以在层之间传输数据。这些 POJO 是 DTO,也就是数据传输对象。
层之间只能使用 DTO,它们不一定相同,例如服务层可能会将业务逻辑应用于从数据访问层接收的 DTO,因此服务层 API 的 DTO 与数据访问层 API 不同。同样,控制器可能会重新排列数据以准备呈现(分组、摘要等),因此发送到 Web 浏览器的数据与从服务层接收的数据不同。
在完全抽象的情况下,数据访问层的 API 不应反映数据访问的技术,即它是否使用 JDBC、JPA、NoSQL、Web 服务或其他一些存储/检索数据的方式。这意味着实体类不应出现在数据访问层之外。
大多数项目不需要这种抽象级别,因此 DTO 通常是一个实体类,并从数据访问层一直流向控制器,在那里它被视图使用,或者被发送到网络浏览器,编码为 JSON。
这取决于项目的规模和复杂性。项目越大,使每一层尽可能抽象/独立变得越重要。
【讨论】:
一个简化的答案可能是:DTO(如果您需要,请参见下文)是一种允许您将特定信息传输到其他地方的东西。这是控制器/适配器/存储库/无论你怎么称呼它的任务。适配器的任务是从外部世界(系统边界之外)获取信息并将这些信息转换为相应的域模型,以便服务逻辑能够使用它。
而且,从我的角度来看,这也适用于存储库:存储库将数据持久化到外部系统(例如数据库甚至另一个 REST 服务),并在请求时将其带回。为此,它可能需要将域模型转换为简化的 DTO,以便能够持久保存在目标系统中。
适配器的想法是,业务逻辑不需要知道如何转换对象以通过 REST/SOAP/MySQL/... 协议表示或传输。这就是适配器的任务。因此:如果需要,DTO 应该留在适配器中。
DTO 是系统内部数据的另一种抽象。您还应该考虑是否真的需要它们。您可能会也可能不会,这取决于您要如何处理这些信息。如果您使用自己编写查询的数据库来保存数据(这意味着您没有使用 ORM 映射器),您可能根本不需要 DTO,因为您可以直接从域模型中提取相关信息。
另一方面,如果您对对象使用反序列化器(例如 Jackson 用于 JSON 或类似的东西),您可能会发现自己需要 DTO,因为这些工具有时需要一些特殊要求才能能够将数据序列化和反序列化为对象。在这里,您可能需要先使用 DTO,然后才能将其转换为域对象,反之亦然。
顺便说一句:这个问题也很好回答on softwareengineering.stackexchange.com
【讨论】:
正确的方法是控制器 -> 服务 -> 实现 -> 存储库
您的存储库层可以返回底层模型,当实现层接收到该模型时,该模型可以转换为您的 DTO。
【讨论】: