DTO:最佳实践
Posted
技术标签:
【中文标题】DTO:最佳实践【英文标题】:DTOs: best practices 【发布时间】:2010-11-26 00:32:09 【问题描述】:我正在考虑使用 DTO 而不是传递我的域对象。我已经在这里和其他地方阅读了几篇文章,我知道有几种方法可以完成这项工作。
如果我总共只有大约 10 个域类,并且考虑到我想在我的视图(WPF 前端)中使用 DTO 而不是域对象来消费,那么推荐的方法是什么。 我认为使用 automapper 等工具对我的情况来说可能有点过头了。所以我正在考虑编写我的自定义映射器类,该类将具有将域类型转换为 DTO 类型的方法。
执行此操作的最佳方法是什么,是否有任何示例可以让我开始执行此操作?
第二个问题:在编写那些将创建 DTO 的方法时,我如何处理设置所有数据,尤其是当域类型引用其他域对象时?我是否在 DTO 中编写等效属性以映射到域类中的那些引用类型? 请问我是否没有用适当的语言提出我的第二个问题。但我想你明白我想问什么。
第三个问题:在编写 DTO 时,我应该编写多个 DTO,每个 DTO 都包含给定域模型的部分数据,以便每个 DTO 都可以用于满足特定 View 的要求,还是应该 DTO 拥有所有数据在相应的模型类中。
【问题讨论】:
准备好为特定的服务方法编写多个特定的数据传输对象,而不仅仅是特定的领域模型。 【参考方案1】:我在这里阅读了一些关于 DTO 的帖子,在我看来,很多人将它们等同于我认为的 ViewModel。 DTO 就是这样,数据传输对象——它是通过网络传递的。所以我有一个网站和服务,只有服务才能访问真正的域/实体对象,并返回 DTO。这些可能是 1:1 映射的,但考虑到 DTO 可能是从另一个服务调用、数据库查询、读取配置等填充的。
之后,网站可以获取这些 DTO 并将它们添加到 ViewModel 中,或者转换为一个。该 ViewModel 可能包含许多不同类型的 DTO。一个简单的示例是任务管理器 - ViewModel 包含您正在编辑的任务对象,以及可以分配任务的一组 Dto.User 对象。
请记住,返回 DTO 的服务可能会被网站、平板电脑或手机应用程序使用。这些应用程序将有不同的视图来利用它们的显示,因此 ViewModel 会有所不同,但 DTO 将保持不变。
无论如何,我喜欢这些类型的讨论,所以请任何人告诉我你的想法。
马特
【讨论】:
只是为了澄清 DTO 不是 ViewModel。它不是一个 DisplayModel,它是一个与 UI 无关的 TransferModel。更重要的是,当您使用 REST 服务传输 DTO 时,它们应该对 UI 结构一无所知。【参考方案2】:我有点在项目中使用 DTO。我倾向于使 DTO 仅显示指定视图所需的数据。我在我的数据访问类中获取视图中显示的所有数据。例如,我可能有一个 Order 对象,它引用了一个 Client 对象:
public class Client
public int Idget;set;
public string Nameget;set;
public class Order
public int OrderIDget;set;
public Client clientget;set;
public double Totalget;set;
public IEnumerable<OrderLine> lines get;set;
然后在我的 OrderListDTO 中,我可能会有类似的内容:
public class OrderListDTO
public int OrderIdget;set;
public string ClientNameget;set;
...
我想在视图中显示哪些字段。我在我的数据库访问代码中获取所有这些字段,因此我不必为我的视图或控制器代码中的实体关联而烦恼。
【讨论】:
如何处理 DTO 对象中的“行”属性?您是让 OrderListDTO 变平还是以某种方式加载“行”集合? 取决于上下文。如果我需要视图中的线条,我会加载它们;如果没有,我不会。有时我的 OrderListDTO 上可能有 LineCount 属性,我执行 LineCount=order.lines.Count(),或者我显示总数:LineSum=order.lines.Sum(t=>t.Quantity)...【参考方案3】:开发 DTO 的最佳方式
开始开发 DTO 的方法是了解它们的唯一目的是将业务实体的数据子集传输到不同的客户端(可以是 UI 或外部服务)。鉴于这种理解,您可以为每个客户端创建单独的包......并编写您的 DTO 类。对于映射,您可以编写自己的映射器定义接口,以将其传递给创建 DTO 对象的工厂,该对象基于将从正在为其创建 DTO 的实体中提取的数据。您还可以定义要放置在实体字段上的注释,但根据个人使用的注释数量,我更喜欢接口方式。关于 DTO 的主要注意事项是它们也是类,并且 DTO 中的数据应该被重用,换句话说,虽然为每个用例创建 DTO 似乎很诱人,但尝试重用现有的 DTO 以尽量减少这种情况。
开始
关于如上所述的开始,DTO 的唯一目的是为客户端提供所需的数据....所以请记住,您可以使用 setter 将数据设置到 dto...或定义一个工厂它基于接口从实体创建 DTO .....
关于您的第三个问题,请按照客户的要求进行操作:)
【讨论】:
【参考方案4】:我用spring-jdbc
来进行项目,并使用了DAO 层。有时现有实体并未涵盖数据库中所有可能的数据。所以我开始使用DTO。
通过应用 '70 结构编程规则,我将所有 DTO 放入单独的包中:
package com.evil.dao; // DAO interfaces for IOC.
package com.evil.dao.impl; // DAO implementation classes.
package com.evil.dao.dto; // DTOs
现在我重新考虑并决定将所有 DTO 作为内部类放在 DAO 接口上,用于无法重用的结果集。所以DAO界面看起来像:
interface StatisticDao
class StatisticDto
int count;
double amount;
String type;
public static void extract(ResultSet rs, StatisticDto dto) ...
List<StatisticDto> getStatistic(Criteria criteria);
class StatisticDaoImpl implements StatisticDao
List<StatisticDto> getStatistic(Criteria criteria)
...
RowCallbackHandler callback = new RowCallbackHandler()
@Override
public void processRow(ResultSet rs) throws SQLException
StatisticDao.StatisticDto.extract(rs, dto);
// make action on dto
namedTemplate.query(query, queryParams, callback);
我认为将相关数据保存在一起(带有 DAO 接口的自定义 DTO)可以使代码更适合 PageUp
/PageDown
。
【讨论】:
【参考方案5】:问题 1:如果您需要传输的 DTO 只是域对象的一个简单子集,您可以使用模型映射器来避免使用无逻辑映射填充您的代码库。但是,如果您需要对映射应用一些逻辑/转换,那就自己动手吧。
问题 2:您可以并且可能应该为您在主 DTO 上的每个域对象创建一个 DTO。一个 DTO 内部可以有多个 DTO,一个用于您需要映射的每个域对象。要映射那些你可以自己做的,甚至使用一些模型映射器。
问题 3:如果您的视图不需要,请不要公开您的所有域。此外,您不需要为每个视图创建 DTO,尝试创建 DTO 来公开需要公开的内容,并且可以重复使用以避免多个共享大量信息的 DTO。但这主要取决于您的应用程序需求。
如果您需要澄清,尽管问。
【讨论】:
【参考方案6】:我将假设您的域模型对象有一个主键 ID,该 ID 可能对应于它们来自的数据库或存储中的 ID。
如果上述情况属实,那么您的 DTO 将克服对其他 DTO 的类型引用,就像您的域对象一样,以外键 ID 的形式。因此,域对象上的 OrderLine.OrderHeader 关系将是 DTO 中的 OrderLine.OrderHeaderId。
希望对您有所帮助。
我能问一下您为什么选择使用 DTO 而不是视图中的富域对象吗?
【讨论】:
DTO 中是否可以包含 ID 属性? - 即样本中的 OrderlineID。我认为 DTO 是完全自包含的数据对象,不会对数据库和其他外部依赖项有任何引用。至于为什么 DTO,我的项目将来会发展成一个大型系统,我想确保我现在构建它以坚持能够在未来通过 Web 服务请求等公开数据。 Beeter 从第 0 天开始就遵循良好的做法,我想。你对我的第三个问题有什么想法吗(我只是在几分钟前添加的)。【参考方案7】:我们都知道Dtos
是什么(可能)。
但重要的是是否过度使用 DTO。
使用Dtos
在“本地”服务之间传输数据是一种很好的做法,但会给您的开发团队带来巨大的开销。
有一些事实:
-
客户端不应看到实体或与实体交互 (
Daos
)。那么你
总是需要 Dtos 来向/从远程传输数据(在进程之外)。
使用Dtos
在服务之间传递数据是可选的。如果您不打算将项目拆分为微服务,则无需这样做。这只是您的开销。
这是我的评论:如果您打算将项目分发给 未来的微服务。或不打算这样做,然后 不要过度使用 DTO
你需要阅读这篇文章https://martinfowler.com/bliki/LocalDTO.html
【讨论】:
以上是关于DTO:最佳实践的主要内容,如果未能解决你的问题,请参考以下文章
向客户端发送数据的最佳实践是啥:返回实体还是 dto? [关闭]