我有一个项目,我们使用屏幕DTO来封装服务层和表示层之间的数据。在我们的例子中,表示层是ASP.Net。
了解DTO的唯一类是服务层类和调用这些服务并显示DTO的页面/控件。
DTO几乎总是特定于页面/控件,因此我觉得它们属于表示层,但这意味着服务层必须引用表示层才能使用DTO。
我几乎认为服务层应该返回更丰富的对象(但不是域实体?)然后表示层可以获取这些对象并将它们映射到每个页面/控件关注点的非常特定的DTO。
这是一个界面声明和一个DTO,所以你可以看到我在说什么:
public interface IBlogTasks
{
BlogPostDisplayDTO GetBlogEntryByTitleAndDate(int year, int month, string urlFriendlyTitle);
}
public class BlogPostDisplayDTO
{
public string Title { get; set; }
public DateTime PostDate { get; set; }
public string HtmlContent { get; set; }
public string ImageUrl { get; set; }
public string Author { get; set; }
public int CommentCount { get; set; }
public string Permalink { get; set; }
}
修改
这是另一个代码示例,用于描述不涉及域模型的用例。也许这会澄清一些事情。我相信我已经超载了DTO的含义。我不是在谈论DTO在线上传输物体的功能。我正在创建DTO以正式化与我的服务层之间的通信合同。
public interface IAuthenticationTasks
{
bool AuthenticateUser(AuthenticationFormDTO authDTO);
}
public class AuthenticationFormDTO
{
public string UserName { get; set; }
public string Password { get; set; }
public bool persistLogin { get; set; }
}
假设我的身份验证突然需要IP地址参数。我现在可以将该属性添加到DTO,而无需更改我的合同界面。
我不想将实体传递给我的表示层。我不希望我的代码能够转到BlogPost.AddComment(new Comment())
答案 0 :(得分:17)
甚至认为'DTO'的规范用例更像是“可以通过网络传递的可序列化对象”,在这种情况下,您实际上更多地指的是“表示传输对象”或“视图模型” ”。
通常,对于我们的项目,这些实际位置的答案是“翻译”代码将DDD域模型映射到PTO类的位置。如果那是在Prensenation层(也许不是那么大的答案)那么pres。层是我声明PTO的地方。但通常情况下,它的“服务”为您进行翻译,这意味着“服务”和“演示”层都需要引用PTO,并且(通常)导致它们在单独的声明中,中性项目/汇编/命名空间/表示层和服务层可以引用的任何内容。
答案 1 :(得分:4)
您使用的是实际服务(网络还是WCF)?如果是,则在服务层中定义DTO,并在添加服务时创建代理(如果使用旧ASMX,则创建Web)将包含所有DTO类型。这是最简单的方法,只维护ASP.NET项目和服务项目之间的松散耦合 - 不需要任何方向的直接项目引用。在更新DTO时,您需要做的就是更新服务引用,它将通过生成的代理类自动将更新公开给您的Web项目。
无论如何,如果您遵循DDD方法,最好让您的基础设施项目(例如特定于Web平台的UI)引用您的域对象,反之亦然。但是,如果您遵循我上面的建议,您的Web项目根本不会直接依赖项目,这是一件好事,当然比根据您的Web项目拥有丰富的域对象更好(如果这是考虑 - 我意识到你并没有说你这样做了。)
如果您的DTO是特定于视图的,那么我会将它们包含在您的UI项目中。控制器的工作应该是确保View只从模型中获取所需内容 - 在您的情况下,只需要一个只包含视图所需字段的值对象。