我有一个富域模型,其中大多数类都有一些行为和一些属性,可以计算或公开成员对象的属性(也就是说这些属性的值永远不会被持久化)。
我的客户端只能通过WCF与服务器通话。
因此,对于每个域实体,我有一个相应的DTO - 一个只包含数据的简单表示 - 以及一个实现DtoMapper<DTO,Entity>
的mapper类,可以将实体转换为其DTO等价物或反之亦然,通过静态网关:
var employee = Map<Employee>.from_dto<EmployeeDto>();
此应用程序的服务器端主要是持久性,我的DTO从WCF服务进入,反序列化,然后任意ORM将它们持久存储到数据库,或者来自WCF的查询请求和ORM执行该查询针对数据库并返回要由WCF序列化和发回的对象。
鉴于这种情况,将持久性存储映射到域实体是否有意义,还是应该直接映射到DTO?
如果我使用域实体,那么流程将是
回程类似
如果我直接映射到DTO,我可以根据请求消除每个对象的一个映射。这样做我会失去什么?
唯一想到的是在插入/更新之前验证的另一个机会,因为我无法保证DTO在通过网络发送之前曾经过验证或者甚至作为域实体存在,我猜有机会验证select(如果另一个进程可能在数据库中放置了无效值)。还有其他原因吗?这些原因是否足以保证额外的映射步骤?
我确实说过上面的“任意ORM”,我确实希望事情尽可能与ORM和持久性无关,但是如果你有任何特殊的东西可以添加到NHibernate,那么一定要做。< / p>
答案 0 :(得分:25)
答案 1 :(得分:2)
无论如何,您需要在客户端映射DTO,因此,为了对称,最好在服务器端进行逆映射。这样您就可以将转换隔离到分离良好的抽象层中。
抽象层不仅适用于验证,而且可以使代码与其下方/上方的更改隔离,并使代码更易于测试且重复次数更少。
此外,除非你注意到额外转换中存在巨大的性能瓶颈,否则请记住:早期优化是所有邪恶的根源。 :)
答案 2 :(得分:2)
您绝对应该将您的域名实体与您的DTO分开,这是他们不同的关注点。 DTO通常是自我描述的自我描述模型,另一方面,您的域实体封装了您的业务逻辑,并附带了很多行为。
说过我不确定额外的映射在哪里? 您使用ORM(也称为域实体)检索数据,并将这些对象映射到DTO,因此只有1个映射?顺便说一句,如果你还没有使用像Automapper那样的东西为你做一些繁琐的映射。
然后将这些相同的DTO反序列化到客户端,然后从那里直接映射到您的UIViewModels。所以大局看起来像是:
答案 3 :(得分:1)
当你说你的服务器端应用程序“主要”关于持久性时,我认为这是要考虑的关键事项。是否真的存在服务器端域模型,需要对其接收的数据进行一些智能处理,或者您的WCF服务是否纯粹充当域模型和数据存储之间的网关?
另外,请考虑您的DTO是否专为客户域设计 这是唯一需要通过您的服务访问该数据存储的客户端域吗? 服务器端DTO是否灵活或粗粒度足以服务于不同的应用程序域? 如果没有,那么可能值得努力保持外部接口实现的抽象。
(DB-&GT; ORM-&GT; EmployeeEntity-&GT; Client1DTOAssembler-&GT; Client1EmployeeDTO)。
答案 4 :(得分:1)
我们有一个类似的应用程序,其中WCF服务主要用作持久数据存储的网关。
在我们的例子中,我们的客户端和服务器不会重用包含“DTO”的程序集。这使我们有机会简单地将代码添加到服务引用生成的部分类中,因此我们通常能够在客户端使用DTO并将其视为域对象。其他时候,我们可能只有客户端的域对象作为我们从WCF服务获得的一堆持久对象的外观。
当您考虑域对象具有的行为和计算属性时,您的客户端和服务器之间究竟有多少重叠?在我们的例子中,我们确定客户端和服务器之间的职责划分意味着客户端和服务器上需要存在的代码很少(如果有的话)(并且完全相同)。
要直接回答您的问题,如果您的目标是保持完全与持久性无关,我肯定会将您的持久性存储映射到您的域对象,然后映射到DTO。有太多的持久性实现可以渗透到您的对象中,并使用它们作为WCF DTO复杂化。
在客户端,如果您只是装饰或扩充DTO,则可能没有必要进行额外的映射,这是一个非常简单的解决方案。
答案 5 :(得分:0)
您的架构似乎经过深思熟虑。我的直觉是,如果您已经决定将对象减少到DTO以通过WCF发送它们,并且您目前在服务器端不需要其他对象功能,为什么不保持简单和映射您的持久性存储直接存储到DTO。
你输了什么?我不认为你真的失去了什么。你的建筑很简洁。如果您将来决定在服务器端需要更丰富的功能,那么您可以随时重新考虑在那里重新创建域实体。
我喜欢保持简单并在以后需要时重新考虑因素,尽量避免预先成熟的优化等等。