数据类应该在层和应用之间重复使用还是映射到特定层的类中?

8
我正在创建一个WPF应用程序,该应用程序使用WCF服务与数据源进行交互。我在客户端和WCF服务器上都使用DI来确保解耦的代码,但我不确定如何处理从后端到用户界面的数据传输。
为了保持分层,目前通过多个映射步骤将数据从数据库传输到UI。在服务器端,数据实体被映射到域对象,然后再映射到服务数据合同。在客户端,WCF代理类被映射到视图模型。
一些同事声称,这种 seemingly identical 类之间的“复制”数据会导致维护问题,因为当有变更时必须更新很多类。相反,他们说我们应该在层之间使用共享类,因为我们控制着客户端应用程序和WCF服务。我也担心涉及的工作量以及潜在的性能损失,但另一方面,在层/抽象之间使用共享类可能会创建紧密耦合的问题。哪种方法最好?

1
在某些情况下,共享WCF数据类可能不可行。例如,如果您使用WCF属性更改服务返回的属性和类型的名称,则生成的类将在WCF服务程序集中定义。这使得所有客户端都依赖于WCF服务以启用共享。仅仅是这样就可以对实际实现的共享量施加现实限制。 - bzlm
3个回答

9
使用DTO作为业务对象并不是您可以做出的最好决定。从我的经验来看,通常情况下,如果您的对象在所有层中都相同,则可能存在架构问题。
在真实的业务场景中,服务器上的业务逻辑和客户端上的业务逻辑很难具有相同的上下文并操作相同的对象。如果它们与数据库完全相同的结构...嗯...听起来像是一个数据驱动的应用程序。
但是,如果它确实是一个数据驱动的应用程序,当客户端访问一些数据、修改它并将其保存回来时,那么您可能真的不需要这个复杂的分层?听起来很简单,所以让我们保持简单。如果确实是数据驱动的应用程序,为什么不在数据库上方创建一个WCF DataServices上下文,并在通过WCF访问数据时让它为您完成所有繁琐的工作,而无需考虑DTO、映射等。
如果不是数据驱动的应用程序,那么您的服务器端可能有一些复杂的业务逻辑,并且这些业务逻辑通常与只在其上下文中有意义的对象一起操作。将这些对象推送到UI毫无意义。

相反,UI可能会发送命令到服务器,以请求系统执行某些操作。例如,它将发送“DisableAccount(id = 123)”命令,而不是加载AccountDTO,将其IsEnabled标志更改为false并将其推回。

如果存在业务逻辑,则可能会由来自客户端的此类命令触发,客户端不需要知道如何禁用帐户或执行其他操作。它只知道并可以向系统发出命令执行某些操作。

因此,在此场景中,客户端(UI)不需要与服务器具有相同的对象。它可能需要一些要显示给用户的数据,但肯定是以对客户端视图有意义的格式而不是业务逻辑的格式。它可能包含一些非规范化的数据,以某种方式组合。

说,UI用户不是映射到用户表的DTO。它是另一个DTO,包含来自不同表格的用户数据和统计信息,并进行了某种处理。客户端不需要知道服务器数据存储的内部结构,因此无需公开它。获取相关数据并发送适当的命令即可。
说出所有这些,我应该强调这不是你做出的二元选择。对于简单的功能,您可以使用简单的方法,对于具有业务逻辑意义的功能,可以执行其他操作。
您不必为每件事都选择一种方法。因此,您不必总是创建三个类似的对象,只是因为这是“正确”的方式,或者总是将实体传递到UI。
但是,您需要清楚地分离上下文,并定义在哪里将使用哪种方法。
在80%的情况下,您可能会得到一些简单的东西(例如WCF DataServices),您不需要做任何事情,这很好,因为在许多操作中,您只想更改数据。

但在另外的20%(也就是你的应用程序的“核心”部分)中,真正的业务逻辑存在 - 在这里,你可能不仅需要对象之间的分离,还需要在各层之间分配责任。


7
所有这些映射确实会产生维护负担。是否有保证取决于您正在构建什么以及业务逻辑的复杂程度。
但是,非常重要的是要认识到一旦您开始在层和层之间共享数据结构,架构就不再解耦。如果您这样做,您本质上正在构建一个单块应用程序。别误解我的意思:如果你只是在做一个光荣的CRUD应用程序,那么构建单块应用程序没有任何问题,但是明确做出这个决定是至关重要的。
至少有以下这些替代方案:
- 保持严格分层。映射成本仍然存在,但代码是解耦的。 - 构建单块应用程序。将所有可以折叠的内容都折叠起来。尽可能保持简单。它将紧密耦合,但它可能变得如此简单,以至于无关紧要。 - 做一些根本不同的事情,比如构建CQRS应用程序或SOA混搭。
就个人而言,我现在更喜欢第三个选项。

领域层中的值对象(如DistancePointTriangleMoneyMailAddress)怎么办?它们的类是否也应该在服务层中复制,以保持严格的分层? - Lightman
@Lightman 我发现在创建分层架构时经常需要这样做,因为值对象应该是不可变的,这往往与边界关注点(如基于事件的GUI或跨网络传输值的(反)序列化)不匹配。 - Mark Seemann

0

我认为层并没有什么神圣不可侵犯的地方。为每个模型实体都创建特定于层的版本会大大增加类的数量,这是不必要的。在我看来,这违反了DRY原则:为什么要重复自己呢?

层纯度能给你带来什么好处呢?

因此,我认为最好的方法是毫无顾虑地传递那些模型实体。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接