我正在(重新)设计大型应用程序,我们使用基于DDD的多层架构。

我们有带有数据层(存储库的实现)、领域层(域模型和接口的定义——存储库、服务、工作单元)、服务层(服务的实现)的MVC。到目前为止,我们在所有层上使用域模型(主要是实体),并且仅将dto用作视图模型(在控制器中,服务返回域模型,控制器创建视图模型,并将其传递给视图)。

I'v read countless articles about using, not using, mapping and passing DTOs. I understand that there's no any definitive answer, but I'm not sure if it's ok or not returning domain models from services to controllers. If I return domain model, it's still never passed to the view, since controller always creates view-specific view model - in this case, it seem legit. On the other hand, it doesn't feel right when domain model leaves business layer (service layer). Sometimes service needs to return data object that wasn't defined in the domain and then we either have to add new object to the domain that isn't mapped, or create POCO object (this is ugly, since some services return domain models, some effectively return DTOs).

The question is - if we strictly use view models, is it ok to return domain models all the way to controllers, or should we always use DTOs for communication with service layer? If so, is it ok to adjust domain models based on what services need? (Frankly I don't think so, since services should consume what domain has.) If we should strictly stick to DTOs, should they be defined in service layer? (I think so.) Sometimes it's clear that we should use DTOs (e.g., when service performs lot of business logic and creates new objects), sometimes it's clear that we should use just domain models (e.g., when Membership service returns anemic User(s) - it seems it wouldn't make much sense to create DTO that is the same as domain model) - but I prefer consistency and good practices.

文章域vs DTO vs ViewModel -如何以及何时使用它们?(以及其他一些文章)与我的问题非常相似,但它没有回答这个问题。我应该用EF在存储库模式中实现dto吗?也类似,但它不处理DDD。

免责声明:我不打算仅仅因为任何设计模式的存在而使用它,另一方面,我想使用好的设计模式和实践,因为它有助于设计应用程序作为一个整体,有助于分离关注点,即使使用特定的模式不是“必要的”,至少目前是这样。


当前回答

如果您返回域模型的一部分,它将成为契约的一部分。合同是很难改变的,因为你上下文之外的事情都依赖于它。因此,您将使部分域模型难以更改。

领域模型的一个非常重要的方面是它很容易更改。这使我们能够灵活地适应领域不断变化的需求。

其他回答

如果您返回域模型的一部分,它将成为契约的一部分。合同是很难改变的,因为你上下文之外的事情都依赖于它。因此,您将使部分域模型难以更改。

领域模型的一个非常重要的方面是它很容易更改。这使我们能够灵活地适应领域不断变化的需求。

我建议分析这两个问题:

Are your upper layers (i.e. view & view models / controllers) consuming the data in a different way of what the domain layer exposes? If there is a lot of mapping being done or even logic involved I'll suggest revisiting your design: it should probably be closer to how the data is actually used. How likely is it that you deeply change your upper layers? (e.g. swapping ASP.NET for WPF). If this is highly unlike and your architecture is not very complex, you may be better off exposing as many domain entities as you can.

恐怕这是一个相当广泛的话题,它实际上涉及到您的系统有多复杂及其需求。

当域模型离开业务层(服务层)时,感觉不对

让你觉得你在把内脏掏出来,对吧?根据Martin Fowler的说法:服务层定义了应用程序的边界,它封装了域。换句话说,它保护了定义域。

有时服务需要返回域中未定义的数据对象

您能否提供此数据对象的示例?

如果我们应该严格遵循dto,那么它们应该在服务层定义吗?

是的,因为响应是服务层的一部分。如果它被定义为“其他地方”,那么服务层需要引用该“其他地方”,从而为您的千层面添加一个新层。

是否可以将域模型一直返回到控制器,或者我们应该始终使用dto与服务层通信?

DTO是一个响应/请求对象,如果您使用它进行通信,那么它是有意义的。如果你在表示层(MVC-Controllers/View, WebForms, ConsoleApp)中使用域模型,那么表示层与你的域紧密耦合,域中的任何变化都需要你改变控制器。

创建与域模型相同的DTO似乎没有多大意义)

这是DTO的缺点之一。现在,您正在考虑复制代码,但随着项目的扩展,它将变得更有意义,特别是在不同团队被分配到不同层的团队环境中。

DTO可能会给应用程序增加额外的复杂性,但层也是如此。DTO是系统的一个昂贵功能,它们不是免费的。

为什么使用DTO

本文提供了使用DTO http://guntherpopp.blogspot.com/2010/09/to-dto-or-not-to-dto.html的优点和缺点

总结如下:

何时使用

用于大型项目。 项目寿命10年以上。 战略,关键任务应用。 大型团队(5人以上) 开发人员在地理上分布。 域和表示是不同的。 减少开销数据交换(DTO的最初目的)

何时不使用

小型至中型项目(最多5人) 项目生命周期为2年左右。 GUI、后台等没有单独的团队。

反对DTO的理由

代码复制。 开发时间成本,调试成本。(使用DTO生成工具http://entitiestodtos.codeplex.com/) 您必须始终同步这两个模型。(就我个人而言,我喜欢这个,因为它有助于了解变化的涟漪效应) 开发成本:额外的映射是必要的。(使用自动绘图器,如https://github.com/AutoMapper/AutoMapper) 为什么数据传输对象(dto)是反模式?

DTO参数

如果没有DTO,表示和域是紧密耦合的。(这适用于小型项目。) 接口/ API稳定 可以通过返回只包含绝对需要的属性的DTO来为表示层提供优化。使用linq投影,你不需要拉出整个实体。 为了降低开发成本,请使用代码生成工具

It seems that your application is big and complex enough as you have decided to go through DDD approach. Don't return your poco entities or so called domain entities and value objects in you service layer. If you want to do this then delete your service layer because you don't need it anymore! View Model or Data transfer objects should live in Service layer because they should map to domain model members and vice versa. So why do you need to have DTO? In complex application with lots of scenarios you should separate the concerns of domain and you presentation views, a domain model could be divided into several DTO and also several Domain models could be collapsed into a DTO. So it's better to create your DTO in layered architecture even it would be the same as your model.

我们应该总是使用dto与服务层通信吗? 是的,你必须通过你的服务层返回DTO,因为你必须在服务层中通过域模型成员与存储库对话,并将它们映射到DTO并返回到MVC控制器,反之亦然。

是否可以根据服务需求调整域模型? 服务只与存储库、域方法和域服务对话,您应该根据自己的需求解决域内的业务,而不是告诉域需要什么。

如果我们应该严格遵循dto,那么它们应该在服务层定义吗?是的,尝试稍后在服务中使用DTO或ViewModel,因为它们应该映射到服务层中的域成员,并且在应用程序的控制器中放置DTO不是一个好主意(尝试在服务层中使用请求响应模式),干杯!

到目前为止,我们在所有层上使用域模型(主要是实体),并且仅将dto用作视图模型(在控制器中,服务返回域模型,控制器创建视图模型,并将其传递给视图)。

由于领域模型为整个应用程序提供了术语(泛在语言),因此最好广泛使用领域模型。

使用ViewModels/ dto的唯一原因是在应用程序中实现MVC模式,以分离视图(任何类型的表示层)和模型(域模型)。在这种情况下,您的表示和域模型是松散耦合的。

有时服务需要返回域中未定义的数据对象,然后我们必须向未映射的域添加新对象,或者创建POCO对象(这很难看,因为有些服务返回域模型,有些则有效地返回dto)。

我假设您谈论的是应用程序/业务/域逻辑服务。

我建议您尽可能地返回域实体。如果需要返回额外的信息,返回包含多个域实体的DTO是可以接受的。

有时,使用第三方框架的人,在域实体上生成代理,会面临从他们的服务中暴露域实体的困难,但这只是错误使用的问题。

问题是——如果我们严格使用视图模型,是否可以将域模型一直返回到控制器,或者我们应该始终使用dto与服务层通信?

我想说在99.9%的情况下返回域实体就足够了。

为了简化dto的创建并将域实体映射到dto中,您可以使用AutoMapper。