gpt4 book ai didi

.net - 是否应该在客户端和服务器端都将DTO映射到域实体或从域实体映射DTO?

转载 作者:行者123 更新时间:2023-12-03 14:39:41 25 4
gpt4 key购买 nike

我有一个丰富的域模型,其中大多数类具有某些行为和某些属性,这些行为和某些属性是经过计算或公开的成员对象的属性(也就是说,这些属性的值永远不会保留)。
我的客户仅通过WCF与服务器对话。
这样,对于每个域实体,我都有一个对应的DTO(仅包含数据的简单表示形式),以及一个实现DtoMapper<DTO,Entity>并可以通过将实体转换为其DTO等效项的映射器类,反之亦然静态网关:

var employee = Map<Employee>.from_dto<EmployeeDto>();

此应用程序的服务器端主要是关于持久性的,在这里我的DTO从WCF服务进入,被反序列化,然后任意ORM将其持久化到数据库,或者从WCF发出查询请求,然后ORM对该查询执行DB,并返回要序列化的对象,并由WCF发送回去。
在这种情况下,将我的持久性存储映射到域实体有意义吗,还是应该直接将其映射到DTO?
如果我使用域实体,流程将是

客户请求对象
WCF将请求传输到服务器
ORM查询数据库并返回域实体
映射器将域实体转换为DTO
WCF序列化DTO并返回给客户端
客户反序列化DTO
DTO通过映射器转换为域实体
创建的视图模型,等等。

回程类似
如果我直接映射到DTO,则可以根据每个请求消除每个对象一个映射。我这样做会失去什么?
唯一想到的是在插入/更新之前进行验证的另一次机会,因为我无法保证DTO曾经经过验证,甚至在通过电线发送之前作为域实体存在,我想这是一个机会。选择时验证(如果另一个进程可能已经在数据库中放置了无效值)。还有其他原因吗?这些原因足以保证采取其他映射步骤吗?
编辑:
我确实在上面说过“任意ORM”,我确实希望事情尽可能与ORM和持久性无关,但是如果您要添加特定于NHibernate的特殊内容,则一定要这样做。

最佳答案

我个人建议将映射保留在服务器端。到现在为止,您可能已经做了很多工作来构建设计。不要扔掉它。

考虑什么是Web服务。它不仅是对您的ORM的抽象;这是合同。它是内部和外部客户的公共API。

公共API应该几乎没有任何理由要更改。除了添加新的类型和方法外,对API的几乎所有更改都是一项重大更改。但是您的域模型不会那么严格。在添加新功能或发现原始设计中的缺陷时,您将需要不时更改它。您希望能够确保对内部模型的更改不会导致通过服务合同进行级联更改。

实际上,由于类似的原因,为每条消息创建特定的RequestResponse类是一种常见的做法(我不会用“最佳做法”来侮辱读者)。扩展现有服务和方法的功能变得更加简单,而又不会破坏更改。

客户可能不希望您在服务内部使用完全相同的模型。如果您是唯一的客户,那么这似乎是透明的,但是如果您有外部客户,并且已经看到他们对系统的解释通常相差甚远,那么您将了解不让完美模型泄漏的价值超出服务API的范围。



有时,甚至不可能通过API发回模型。发生这种情况的原因有很多:


在对象图中循环。 OOP非常好;序列化的灾难。最后,您不得不为必须串行化图形的“方向”做出痛苦的永久性选择。另一方面,如果您使用DTO,则可以按照所需的任意方向进行串行化,无论适合什么任务。
尝试在SOAP / REST上使用某些类型的继承机制充其量不过是一种麻烦。旧式XML序列化程序至少支持xs:choiceDataContract不会,我也不会质疑其基本原理,但是只要说您的富域模型中可能存在某种多态性就可以了,这简直是不可能实现的。
延迟/延迟加载,如果您使用ORM,则可以利用它。确保将其正确序列化非常尴尬-例如,使用Linq to SQL实体,WCF甚至不会触发惰性加载器,除非您手动加载,否则它只会将null放入该字段中-但问题会对于返回的数据更糟。像在构造函数中初始化的List<T>自动属性那样简单(在域模型中很常见),根本无法在WCF中使用,因为它不会调用构造函数。相反,您必须添加[OnDeserializing]初始值设定项方法,并且您确实不想用此垃圾弄乱您的域模型。
我还刚刚注意到您在使用NHibernate的括号中的注释。考虑到像IList<T>这样的接口根本无法通过Web服务进行序列化!如果像我们大多数人一样,将NHibernate与POCO类一起使用,那么这段时间根本就行不通。




当内部域模型完全不符合客户端需求时,很可能会有很多实例,并且更改域模型来满足这些需求是没有意义的。举个例子,让我们简单地处理发票。它需要显示:


有关帐户的信息(帐号,名称等)
发票专用数据(发票编号,日期,到期日等)
应收帐款信息(以前的余额,滞纳金,新的余额)
发票上所有内容的产品或服务信息;
等等。


这可能适合领域模型。但是,如果客户希望运行显示1200张发票的报表怎么办?某种对帐报告?

这很糟糕,需要序列化。现在,您将发送1200份发票,其中一遍又一遍地序列化相同的数据-相同的帐户,相同的产品,相同的应收账款。在内部,您的应用程序正在跟踪所有链接。它知道35号发票和45号发票是针对同一客户的,因此共享Customer参考;所有这些信息在序列化时都会丢失,最终您将发送大量的冗余数据。

您真正想要的是发送包含以下内容的自定义报告:


报告中包括的所有科目及其应收账款;
报告中包括的所有产品;
所有发票,仅包含产品和帐户ID。


如果要避免大量冗余,则需要先对传出数据执行其他“规范化”,然后再将其发送到客户端。这在很大程度上有利于DTO方法。在您的域模型中使用这种结构是没有意义的,因为您的域模型已经以其自己的方式处理了冗余。

我希望这些例子和理由足以说服您保持完整的Domain <-> Service Contract映射。到目前为止,您已经做对了绝对正确的事情,拥有了出色的设计,并且将所有这些努力全部取消而取而代之的是一种可耻的行为,而这可能会导致以后引起很多头痛。

关于.net - 是否应该在客户端和服务器端都将DTO映射到域实体或从域实体映射DTO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2201150/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com