如何避免客户端和服务器之间重复业务逻辑?

问题描述 投票:19回答:9

随着Web应用程序需求的增长,我发现自己编写了越来越多的API驱动的Web应用程序。我使用像AngularJS这样的框架来构建与这些API通信的富Web客户端。目前我使用PHP(Lumen或Laravel)作为服务器端/ API。

问题是,我发现自己经常在客户端和服务器端之间重复业务逻辑。

当我说业务逻辑时,我的意思是订单表格的规则如下:

  • 如果你买Y,你可以买X.
  • 如果你有Z,你不能买Y.
  • 如果您购买其中的10件,您将获得10%的折扣。
  • 高度x宽度x深度x成本=最终成本。
  • 如果宽度大于5,高度必须在10到20之间。
  • 等等

为了使这个应用程序既响应又快速,计算逻辑(以及其他业务逻辑)正在客户端完成。由于我们不应该信任客户端,因此我在服务器端重新验证这些数字。这种逻辑变得相当复杂,在这两个地方编写这种复杂的逻辑会感觉很危险。

我有三个解决方案:

  1. 使需要业务逻辑的所有内容都对API进行ajax调用。所有业务逻辑都将存在于一个地方,并且可以进行一次测试。这可能很慢,因为客户端必须等待他们对订单表单进行的每次更改才能获得更新的值和结果。拥有一个非常快的API将有助于此。主要缺点是,当用户连接不良(移动设备)时,这可能无法正常工作。
  2. 在客户端和服务器端编写业务逻辑。客户端在表单上进行更改时会获得即时反馈,并且我们会在服务器上提交后验证所有数据。这里的缺点是我们必须复制所有业务逻辑,并测试双方。这当然是更多的工作,会使未来的工作变得脆弱。
  3. 相信客户!?!在客户端编写所有业务逻辑,并假设他们没有篡改数据。在我目前的情况下,我正在研究一个引用构建器,它总是得到人类的审查,所以也许这实际上是可以的。

老实说,我对任何解决方案都不满意,这就是我向社区寻求建议的原因。我很想听听你对这个问题的意见或方法!

php angularjs ajax laravel api-design
9个回答
8
投票

你还可以做一件事。

仅使用JavaScript创建验证和业务逻辑代码。但是要把它作为松散耦合。如果可能,只将JSON作为输入,JSON作为输出。

然后在PHP服务器旁边设置一个nodejs服务器,将这些逻辑提供给客户端。因此,在客户端,它可以在没有AJAX调用的情况下使用。

然后从服务器端(PHP),当您需要验证并运行所有这些业务逻辑时,将cURL调用到nodejs以验证这些数据。这意味着它是从PHP服务器到nodejs服务器的http调用。 Nodejs服务器将有另一个代码,它将获取这些数据并使用相同的代码进行验证并返回结果。

通过这种方式你可以做到

  1. 更快的开发(一个单元测试您的逻辑)
  2. 更快的客户端代码执行(不需要ajax,因为nodejs向客户端提供相同的验证javascript文件)
  3. 所有业务逻辑都将转到nodejs服务器。 (当业务逻辑发生变化时,您只需触摸此部分,以便在不久的将来,如果您还需要创建其他接口,则可以使用此服务器验证您的数据。它将像您的业务规则服务器一样工作)

您需要做的只是在PHP服务器旁边设置一个nodejs。但是您不需要将所有代码更改为nodejs服务器。


8
投票

当我决定使用Laravel创建一个用于后端的应用程序和用于前端的Angular 2时,我遇到了同样的问题。在我看来,到目前为止还没有避免业务逻辑重复的解决方案,因为:

目前,PHP和JavaScript无法从一个转换为另一个。如果我们可以使用相同的语言编写业务逻辑然后将它们嵌入到后端和前端,那会不会很好。从这一点来看,它引出了另一个观点:

为了实现这个目标,我们应该只用一种语言编写业务逻辑,到目前为止JavaScript是最好的解决方案。如您所知,TypeScript / EMCA Script帮助我们以OOP方式编写代码。 Meteor框架NodeJS基础架构帮助我们用JavaScript编写代码,以便在后端和前端运行。

因此,从我的角度来看,我们可以使用TypeScript / EMCA来编写业务逻辑包,例如,用JavaScript编写的验证类可以在两侧实现,所以你只需要写一次,但是它会被调用两次前端和后端也。

这是我的观点。希望看到这个非常有趣的话题的其他解决方案。


5
投票

一种可能的解决方案是以XML或JSON Schema等声明性抽象语言声明验证规则。

然后在客户端,比如AngularJS - 您可以将这些规则转换为现成的表单渲染器。因此,现在在客户端,您最终会得到验证声明规则的表单。

然后在服务器端API上,您需要创建一个可重用的验证引擎,该引擎将根据定义的规则进行验证。

您最终得到的是一个单独的地方,您的JSON模式或您声明性地定义规则的地方,您的表单和验证规则已定义。


3
投票

当我参与一些自己的项目时,我也处于这个位置。使用客户端设备的强大功能来进行繁重的工作总是很诱人,然后只是在服务器端验证结果。这将导致业务逻辑出现两次,包括前端和后端。

我认为选项1是最好的选择,它是最有意义的,也是最合乎逻辑的。如果您希望将来将Web应用程序扩展到本机移动应用程序,则可以通过调用这些API来重用所有业务逻辑。对我而言,这是一次巨大的胜利。

如果担心如果提出过多的API请求会影响移动性能,那么可能会尝试将一些请求组合在一起并在最后执行一次检查?因此,不要检查表单中的每个字段,而是在用户提交整个表单时进行检查。如果你继续请求和响应数据最少,大多数互联网连接就足够了,所以我不担心这个。

我通常遇到的一个更大的问题是,因为您的Web应用程序将分为几个部分,每个部分都会调用相关的API。应用程序的状态要理解起来要复杂得多,因为用户可以在这些状态之间跳转。您需要仔细考虑用户旅程并确保流程没有错误。

以下是我必须处理的一些常见问题:

  • 如果API返回一个,前端是否会显示错误?
  • 如果用户犯了错误并提交了表格,他/她应该看到错误。但是一旦用户修复了错误并再次提交,错误就应该隐藏,现在应该显示成功消息。
  • 如果API有问题或互联网连接不稳定怎么办,所以不会返回任何内容。前端会挂吗?
  • 如果有多个错误消息怎么办?前端可以/显示它们吗?

我建议在前端进行大量的单元测试,以确保它稳定,即使业务逻辑只在后端。


1
投票

首先:永远不要相信客户。

话虽如此,我一直在处理这个问题,遗憾的是我找不到一个简单的解决方案。你需要在双方进行验证,但是,你不需要对它们进行全部验证。

我所做的是尝试平衡它。在客户端,你做大部分简单(但有价值)的验证,正常的东西,数字必须是数字,日期必须是日期,范围内的数据等,所以当你提交它时,它会到达服务器以获得完全经过验证,但是你确保在客户端,大多数信息至少是以其正确的格式,并且其中一些(或大部分)已经过验证,但是,真正的业务逻辑是在服务器端完成的,但由于大多数数据已经正确,服务器端验证很可能会批准该请求,因此您将避免大量重新提交。

现在,如何做到这一点,当你需要改变一些东西时,你不需要在两边都改变它?好吧,有时你无法避免这种情况,当需要进行重大更改时,可以共享业务逻辑参数,就像你建议的那样,这可以通过ajax来完成。你创建一个php文件,你有你所有的业务逻辑参数,并且ajax请求你只在客户端加载一次(当加载脚本时),你需要优化它,所以你只得到参数值,其他一切应该已经存在于客户端,因此如果业务逻辑中的某些参数值发生更改,则只需在参数文件中更改它。 (如果在加载脚本后更改了参数,验证将在服务器端失败,现在您需要决定是否强制它们使用reaload脚本,因此参数是现实的,或者不是,我让它们重新加载它们)

我想你应该已经明白了。这就是我所做的,它对我来说非常好,为我节省了大量的重新编码。

我希望你觉得这有帮助。


1
投票

今天解决方案显然是@ParthaSarathiGhosh的解决方案,但不久的将来肯定会给我们另一个解决方案......

WebAssembly是一种低级汇编语言,可以与您的应用程序一起部署并在浏览器中运行。它允许您通过调用程序集中的已编译代码从JavaScript请求一些逻辑。对于运行客户端的重型脚本,建议使用此方法,但同时允许您在前端重用后端代码。通过这种方式,您将能够为后端编写逻辑,并在前端重用它。

今天,大多数现代浏览器都支持这项技术,但它只能从c / c ++中获得。如果你有这些技能,你就可以使用它了。

它当然也计划将其扩展到其他语言(因为已经有一些关于c#的研究 - 例如:blazor - 和其他语言)。但成熟度水平似乎不够稳定(即使是blazor开发团队也不建议将其用于生产)。

这只是我自己的意见,但=> NodeJS中的逻辑是重用javascript代码的解决方案,但是当涉及到大型可维护逻辑代码时,我仍然觉得需要强类型语言。 (是的,我知道TypeScript,它真的很棒,但我想念一些东西)。 WebAssembly仍然有点年轻,但肯定会为尊重DRY原则带来重大改进。


0
投票

我觉得选择1是未来最好的选择。 API第一次开发允许测试所有业务逻辑并正常工作并允许接口访问。你永远不应该相信用户!

与为所需的每个接口一次又一次地编码相同的逻辑相比,电源API的第一次开发是无限的。


0
投票

这是关于是将逻辑客户端还是服务器端的类似线程。在一天结束时,每种情况都是独一无二的,并保证有不同的计划,但在这个主题中有一些好的,指导性的提示。

Client-side vs. Server-side


0
投票

非常有趣的问题 - 另一个警告可能是我们想要支持离线模式,即app也必须离线运行。

另一个更复杂的问题是,如果让你说好的服务器端是一个技术,如java或.Net等,而在客户端,你选择的是原生工具或Xamarin之类的东西,但遗憾的是服务器不一样。

因此,Partha的方法看起来最有希望 - 但正如所述,这在完全离线模式下不起作用。因此,稍微修改的方法是将验证规则视为数据。但不是简单的数据 - 而是说“整个该死的代码就是数据”。您可以选择任何您喜欢的解释代码语言 - Groovy,JavaScript,CScript等 - 但是您将遵循的一条规则是所有商业逻辑都是该代码!

如果你能够实现这一点,那么在离线模式下 - 当你同步数据时 - 你也将同步这种非常特殊的数据类型,即代码! (所以没有“信任”客户的风险)

然后离线API和在线API是100%相同的代码 - 但代码是我们的解释语言。我认为这种方法不仅可以解决这个问题,还可以使业务逻辑维护更加简单。我们经常创建高度复杂的数据模型来支持规则;事实上在2019年 - 你可以简单地用ifs / elses创建规则,它会更简单。我们可以用一个非常简单的脚本工具训练最终用户,并且可以用更少的代码来做更好的事情。

我已经把这些想法的博客文章放在一起:https://medium.com/@thesaadahmad/business-logic-conundrum-offline-mobile-apps-a06ecc134aee

© www.soinside.com 2019 - 2024. All rights reserved.