题 我应该在Web API服务和它的客户端之间共享类型吗?还有什么其他选择?


我们正在开发Web API RESTful服务,以便为我们企业的所有应用程序提供对公共数据的访问。为了帮助我们,我们还将发布一个客户端API,它封装了所有HttpClient详细信息,并提供了对数据的强类型访问。

我们的目标是开始小规模并逐步添加功能,同时仍然保持与已部署版本的客户端API的向后兼容性(与相同主要版本的客户端兼容)

在谈论设计时,我们的团队只是进行了很长时间的讨论 我们是否应该在服务器和客户端之间共享类型 (例如,通过服务器和客户端都依赖的版本化NuGet包),最终有利有弊......我们无法决定这种或那种方式。

在客户端和服务器之间共享类型(共享程序集)

优点

  • 客户端模型和服务器模型始终是最新的
  • 没有序列化/反序列化问题,因为相同类型被序列化/反序列化
  • 没有重复

缺点

  • 需要找到一种方法来共享服务器和客户端之间的类型
  • 非语义修改可以破坏现有的客户端应用程序(在服务器模型中更改类的名称或其名称空间),即使它对序列化的json没有影响,因此应该没有影响
  • 没有意识到打破克林的风险

客户端和服务器的单独(但结构上等效)类型

优点

  • 客户端“模型”较少耦合到服务器实现(只是服务器的Json输出的镜像,但没有硬“相同类型”关系)
  • 服务器模型可以发展而没有破坏任何客户端的风险
  • 能够独立于服务器模型增强客户端模型
  • 客户端模型是客户端包的一部分,没有“共享包”来维护服务器和客户端之间

缺点

  • 服务器代码和客户端代码之间的重复
  • 保持服务器端和客户端结构同步的容易出错的任务

我们团队的每个解决方案似乎都有50/50的偏好。

我个人偏好第二个选项,因为我认为RESt都是关于解耦的,而解耦意味着客户端不应该关心服务器端是如何实现的(无论是哪种类型,或者它是否是.NET应用程序),而是希望我们可以摆脱可能的重复,也许可以归功于代码生成或类似的东西,但无法找到关于这个主题的任何指导

在客户端和服务器之间共享类型还有其他优缺点吗? 

如果我们不分享它们, 有没有办法降低维护成本 当试图保持 客户端模型和服务器模型同步 ?


37
2018-02-26 16:21


起源


在这种情况下,人们经常忽略的一个方面是,有时您正在进行点对点应用程序,在这些应用程序中,您无法轻松地将服务器和客户端分开。在那种情况下,代码重复是非常严重的问题...没有简单的回答“什么是正确的事情”。 - kape123
只需添加关于此主题的Twitter对话的链接: twitter.com/UdiDahan/status/933381212764360704 - tsimbalar
松散耦合的系统可能会变得可怕。如果您需要进行重大更改,并且如果您无法充分识别使用该服务的客户端,那么您可以做的最好的事情是进行更改并等待系统崩溃时开始尖叫。我认为添加共享组件有助于识别松散系统的客户端,因此可以跨服务器和客户端主动管理更改... - barrypicker


答案:


我认为,如果你不小心,第二个选项最终可能比第一个选项更少RESTful。 REST不是关于解耦,而是关于管理和聚焦客户端和服务器之间的耦合。

在一个安静的系统中,您知道客户端和服务器之间的耦合在于媒体类型定义和链接关系定义。

在这两个选项中,您实际上是在客户端和服务器之间共享类型。在第一个选项中,此共享通过具体实现进行显式化,该实现可以作为nuget包进行管理,并且可以独立于客户端和服务器进行版本控制。

在第二个选项中,您有两种共享类型的实现。但是,我猜你没有计划定义一个明确定义这些类型属性的媒体类型。因此,您没有单一的事实来源,您无需定义客户端和服务器之间的数据协定。你怎么知道什么时候你会做出一个会打破客户的改变?至少对于共享库,您可以知道服务器现在使用的是1.4.7版的共享类型,而客户端正在使用1.3.9。您可以在共享类型库上使用语义版本控制,以了解何时进行重大更改以强制客户端更新。

使用第二个选项,您有一个客户端和一个将独立版本化的secer,并且很难跟踪这两个版本之间是否存在重大变化。

Explict媒体类型始终是捕获合同并在HTTP客户端和服务器之间对合同进行版本化的最佳方式。但是,如果您不想去那里,那么共享nuget库是最好的下一步,因为您正在隔离从客户端和服务器实现共享的系统部分。这是REST的主要目标之一。您实际上共享该共享协定的实现库这一事实仅影响在不能使用该库的其他平台上的消费者。

我创造了这个词 Web Pack 几年前,我想描述使用共享nuget包来包含共享耦合的想法。我写了几篇文章 这里 和 这里 就此主题而言。


22
2018-02-26 18:50



鉴于您是微软人,我看到您提倡在客户端和服务器中共享的强类型定义。这遵循微软的历史方法,可追溯到分布式COM,以及WCF(SOAP)。非常喜欢RPC。 WebApi利用默认模型绑定器支持这种意识形态。我已经与非以微软为中心的开发人员进行了讨论,并发现有时会推动另一种方式 - 减少共享,减少类似RPC,服务从有效负载中获取尽可能少的复杂性和框架。这似乎已成为一个宗教辩论主题。不确定我的立场...... - barrypicker
我主张使用媒体类型来传达语义。但是,当提出两个依赖于保持基于单一语言和平台的兼容类型库的选项时,我提倡最简单和最可靠的解决方案。 - Darrel Miller


我们进行了类似的讨论 - 具有类似的优点和缺点 - 我们采取了混合方法。我们在客户端和服务器之间共享一个程序集,但只共享接口。然后我们基于客户端的接口创建了类。优点是客户端和服务器上的实际对象可以独立更改。


9
2018-03-31 18:14