使用 DTO 作为业务对象并不是您可以做出的最佳决定。根据我的经验,我可以说,通常当所有层的对象都相同时,某处的架构可能存在问题。
在真实的业务场景中,服务器上的业务逻辑和客户端上的业务逻辑不太可能具有相同的上下文并使用相同的对象进行操作。如果它们与数据库具有完全相同的结构……嗯……听起来像是一个数据驱动的应用程序。
但是如果它是一个数据驱动的应用程序,当客户端访问一些数据,修改它并保存回来,那么你可能真的不需要这种复杂的分层?这听起来很简单,所以让我们保持简单。如果它是一个数据驱动的应用程序,为什么不在你的数据库之上创建一个WCF DataServices上下文,当你通过 WCF 访问你的数据而不考虑 DTO、映射等时,让它为你做所有的脏活。
如果它不是一个数据驱动的应用程序,那么你的服务器端可能有一些复杂的业务逻辑,而这个业务逻辑通常与只对它的上下文有意义的对象一起操作。将这些对象一直推送到 UI 是没有意义的。
相反,UI 可能会向服务器发送命令以要求系统做某事。例如,它将发送“DisableAccount(id=123)”命令而不是加载 AccountDTO,将其 IsEnabled 标志更改为 false 并将其推回。如果有业务逻辑,那么它可能会被客户端的这种命令触发,不需要知道如何禁用帐户或如何做其他事情。它只知道并且可以命令系统做某事。
因此,在这种情况下,客户端(UI)不需要与服务器相同的对象。它可能需要一些数据来显示给用户,但它肯定会采用对客户视图有意义的格式,而不是对业务逻辑有意义的格式。它可能会包含一些非规范化的数据,以某种方式组合在一起。
比如说,用户界面的用户不是映射到用户表的 DTO。它是另一个 DTO,包含来自不同表的用户数据和统计信息,以某种方式处理。客户端不需要知道服务器数据存储的内部结构,所以不需要暴露它。获取相关数据并发送相应的命令,就是这样。
说了这一切,我应该强调这不是你做出的二元选择。对于简单的功能,您可以使用简单的方法,对于具有业务逻辑的功能,您可以做其他事情。
您不必为所有事情选择一个。因此,您不必总是因为它是“The Way”而创建 3 个相似的对象,或者总是将实体一直传递到 UI。但是您必须做的是清楚地分离上下文并定义将使用哪种方法。
在 80% 的情况下,您可能最终会得到一些简单的东西(例如 WCF DataServices),并且您不需要做任何事情,这很好,因为在很多操作中您只想更改数据。
但是在真正的业务逻辑所在的其他 20%(这是应用程序的“核心”)中 - 在这里,您可能希望这种分离不仅用于对象,还用于层之间的职责。