DTO形状:扁平、复杂/嵌套,或两者混合。

24

我有一个MVC2 n-tier应用程序(DAL,Domain,Service,MVC web),采用DDD方法(领域驱动设计),具有带有仓储的领域模型。我的服务层使用请求/响应模式,其中请求和响应对象包含DTO(数据传输对象)以从一层传输数据到另一层,并且借助AutoMapper进行映射。我的问题是:DTO通常应该采用什么形式?它可以包含嵌套/复杂的DTO吗?还是严格限制为平面投影?或者可能是两者混合?此外,拥有平面DTO与更复杂/嵌套的DTO之间的主要原因是什么?

例如,假设我有如下域:

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Company Company { get; set; }
}
public class Company
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

我考虑过三种不同的方式来建模响应对象。

选项1 - 最符合DRY原则的选项:

public class GetEmployeeResponse
{
    public class EmployeeDTO { get; set; } // contains a CompanyDTO property
}

根据我所做的研究,一个DTO如果采用与上述领域对象类似的形式将是不合适的。

选项2 - 领域的扁平化投影(反-DRY):

public class GetEmployeeResponse
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string CompanyName { get; set; }
    public string CompanyAddress { get; set; }
    public string CompanyCity { get; set; }
    public string CompanyState { get; set; }
}

这更简单,像DTO应该有的那样,但最终会产生更多的DTO。

选项3 - 两者的混合:

public class GetEmployeeResponse
{
    public EmployeeDTO Employee { get; set; }
    public CompanyDTO Company { get; set; }
}

这样可以让代码更加DRY(避免重复),可重用和易于管理,并且不会将我的域结构暴露给最终用户。另一个主要优点是,其他响应,例如GetCompanyResponse可以简单地返回CompanyDTO,而无需复制所有那些属性,类似于选项2。你认为呢?你采取了哪个选项(如果有的话)并且是否适用于你?如果这些请求/响应后来作为WCF服务方法公开,您的答案会改变吗?


1
你为什么要首先构建n层MVC应用程序?我并不是说这样做是错误的。只是好奇通过在领域模型和Web层之间放置服务,您能获得什么优势。 - Szymon Pobiega
1
我只是想回应你提出的一个具体评论:“复制所有这些属性”。一旦您的系统达到一定的复杂度阈值,最好拥有一个专用的读模型,在数据库级别上进行去规范化处理(通过视图或在ORM配置中)。当我开始这样做时,它使我能够构建更复杂的领域模型,因为我不必担心为查询方面的事情而使它们变得昂贵。我的意思是,如果您只是要去规范化它们,为什么要使多个模型变得昂贵?让数据库来完成这项工作。这正是它擅长的。 - Ryan
@Szymon,有一个服务层有很多好处。对我来说,最大的优点是我可以将所有安全性放在一个层中,不让它泄漏到我的控制器中。 - Ryan
除了 @Ryan 的评论之外,我的服务层 API 最终将作为 WCF 服务向合作伙伴公开使用和开发。我的 Web 应用程序将成为另一个使用这些 Web 服务的客户端。 - tbehunin
1
@tbehunin 你怎么知道你的合作伙伴会发现你为 Web 接口设计的 API 有用呢?在我看来,这个机会非常小。我宁愿直接将 UI 与领域进行连线,并构建仅专注于外部系统自动化使用的服务 API。 - Szymon Pobiega
显示剩余2条评论
1个回答

15

我的个人偏好是尽可能保持数据传输的平坦,只传输所需的数据。话虽如此,过去我曾使用深度嵌套的DTO,因为当时很有意义并符合要求。所以,我想这取决于具体情况。在一天结束时,请根据当前应用程序的需要进行选择。没有必要试图将不适合实现目标的数据硬塞到DTO约定中。


不要为了实现一个模糊和含糊的设计模式(DTOs),而抛弃常识。 ;) 你如何在平面DTO中存储分层信息(产品类别)? - John Farrell
@jfar 我关注的因素之一是应用程序的喋喋不休。如果网络上有大量流量(即客户端请求许多小信息块)只为显示一个信息页面,则我将尝试将其合并为一个信息请求,并以适当的格式返回数据。 - Nathan Fisher
如果适合的话,我可能会去非规范化,这样你就可以将类别作为产品的一部分包含进去。正如我在我的答案中所说,我更喜欢尽可能保持平坦,但这并不意味着它应该像煎饼一样平坦。如果使用层次结构的DTO使得您在所选语言中编写和维护代码更容易,那么我会选择这条路线,就像我以前在使用带有MS InfoPath的Web服务时所做的那样。在那种情况下,试图展开DTO是没有意义的。应用程序的数据需求定义了DTO。 - Nathan Fisher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接