我们应该在服务器和客户端上使用相同的业务类吗?

Posted

技术标签:

【中文标题】我们应该在服务器和客户端上使用相同的业务类吗?【英文标题】:Should we use the same business classes on server and client? 【发布时间】:2009-11-16 14:46:04 【问题描述】:

假设您在服务器应用程序上有业务域模型,并且您将开发富客户端应用程序。

您可以使用 DTO 将数据传输到客户端并将更改传输到服务器或使用 WCF 服务并在客户端上生成新类。

另一种方法是在业务逻辑层传输与您在服务器上使用的对象相同的对象。这些也可以是 ORM 使用的类。在这种情况下,类不应包含特定于服务器的逻辑,但它们可以包含一些通用逻辑。

我的问题是:

您使用哪种变体,您建议在新项目中使用哪种变体? 哪个更好? 在某些情况下第二个更好吗?您如何描述这些情况? 有多少应用程序使用第一种/第二种方法? 具体应用如何选择?

【问题讨论】:

Alex,您只是在询问 DTO 在与面向服务的架构交互和自己公开服务方面的作用吗? 我没有清楚地理解你的问题,但我认为答案是肯定的......我的目标是决定应该如何设计三层应用程序,一般来说哪种方式是最好的。跨度> 谁应该知道有多少应用程序使用哪种方法?每个人都只能谈论他参与的项目。 【参考方案1】:

我们很难在我们的项目中找到这个问题的确切答案。故事是这样的:

我们认为重用类非常棒,可以减少许多重复的东西。 NHibernate 允许从会话中分离和重新附加类,所以这似乎很简单。

我们遇到的第一个也是最大的问题是您无法发送整个数据库,因此我们必须将实体模型拆分为多个部分,并通过 guid 而不是普通引用将它们链接起来。这使查询变得非常复杂。

我们不得不实现一些技巧,因为序列化、持久性和数据绑定都有它们的问题。这个相当丑陋的黑客都进入了同一个班级。它们变得越来越大。

属性似乎是无害的,直到您看到每个类和每个属性的大量属性列表,因为每个层都添加了它的属性。

实体隐式开始支持两种“模式”:DTO 模式持久性模式。当PrepareSerializationAfterDatabaseRetrieval 等方法出现时,这一点就很明显了。有些属性只能在服务端使用,有些只能在客户端使用。

显然,维护变成了一场噩梦。没有人再冒险更改实体了,因为您必须更改整个系统中的内容。

然后我们开始切换到 Dtos。

经过大量工作,我们设法重写了系统的一些重要部分以使用 Dtos。而且——突然间,每个人都高兴了。

您可以维护序列化。您可以维护数据库模型并优化查询。您可以在不破坏任何东西的情况下对 cient 模型进行更改。

结论:与在所有层中使用相同类时失去可维护性相比,为每一层维护相似类的努力是荒谬的。

还有一些琐碎的实体和值类型的类同时用作实体和Dtos。

我可以想象一个只包含琐碎实体的小型应用程序可以在没有 Dtos 的情况下生存。

【讨论】:

有趣 - 您在 PrepareSerializationAfterDatabaseRetrieval 方法中添加了哪些行为?您的领域模型类是否包含序列化、持久性和数据绑定逻辑?或者它们是否包含所有这些逻辑,而您发现自己正在转向 DTO 以将其中的一些外部化? 例如,WCF 将 IList 转换为数组。因此,属性设置器必须将它们转换为列表 - 当 NHibernate 使用相同的属性设置器来分配其持久列表的实现时,它不会制动。一些引用仅用于传输,它们是存储在其他地方并在 AfterDatabaseRetrieval 方法中加载的数据的冗余。有些类型不能被序列化,例如Type,所以它需要另一个字符串属性。以此类推,千千万万个小问题,都有一个原因,一个历史。结论只是这些类需要服务于太多的目的。【参考方案2】:

您应该编写尽可能少的符合您要求的类。这样一来,您就不太可能重复自己,需要测试的东西也更少,并且在您需要更改代码时出错的事情也更少。

如果您的客户端代码确实需要执行域逻辑,您应该:

1) 直接反序列化为域模型类(尤其是在您使用支持持久性无知的 ORM 时)。

// Customer is a business object / aggregate root with domain logic
ICustomer customer = customerRepository.Get<Customer>(customerId);

2) 使用数据传输对象来初始化您的领域模型类:

// Given a customer data transfer object
ICustomer customer = new Customer(customerDto);

如果没有必要,我宁愿不写 DTO,所以我更喜欢第一种方法。但有时 DTO 是必要的,例如当您使用由服务代理生成的自动生成的类时 - 或者如果您是公开自己的 DTO 的服务。

如果客户端不应该执行业务逻辑,它永远不需要知道您的域模型类。在这些情况下,客户端应使用数据传输对象或自定义视图模型类。

【讨论】:

【参考方案3】:

我必须同意前两个答案。但是,请注意保持 BO/DTO 类同步的工作(我会考虑使用代码生成)。

请参阅我的回答 here,了解那些想朝另一个方向发展的人 - 他们想将众多 DTO/BO 整合到他们的系统中。

【讨论】:

以上是关于我们应该在服务器和客户端上使用相同的业务类吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在客户端和服务器之间共享 Javascript 业务规则?

我应该为业务逻辑创建一个 dll 还是多个 dll?

Windows 客户端和 Web 客户端 - 相同的业务层和数据层

在客户端加密并在 WCF REST 中的服务器上解密相同

在 iOS 上使用 Parse BaaS:我应该使用 PFFile 来保存原始类型和 Objective-C 类吗?

gRPC:我应该为整个应用程序使用单个客户端吗?