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

时间:2016-05-28 18:56:25

标签: php angularjs ajax laravel api-design

随着网络应用程序需求的增长,我发现自己编写了越来越多的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. 相信客户!?!在客户端编写所有业务逻辑,并假设他们没有篡改数据。在我目前的情况下,我正在研究一个引用构建器,它总是得到人类的审查,所以也许这实际上是可以的。

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

9 个答案:

答案 0 :(得分:8)

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

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

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

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

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

答案 1 :(得分:8)

你还可以做一件事。

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

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

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

通过这种方式你可以制作

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

答案 2 :(得分:5)

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

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

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

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

答案 3 :(得分:3)

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

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

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

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

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

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

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

答案 4 :(得分:1)

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

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

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

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

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

我希望你觉得这很有用。

答案 5 :(得分:1)

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

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

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

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

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

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

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

Client-side vs. Server-side

答案 8 :(得分:0)

一个非常有趣的问题-另一个警告是我们要支持离线模式,即应用程序也必须离线运行。

如果让您说好的服务器端全部使用Java或.Net等技术,而在客户端上,您正在本机工具或Xamarin之类的东西之间进行选择,但是不幸的是,它与服务器不一样。 / p>

因此,Partha的方法似乎最有前途-但正如所说的那样,这在完全脱机模式下不起作用。因此,略微修改的方法是将验证规则视为数据。但不是简单的数据-而是说“整个该死的代码就是数据”。您可以选择喜欢的任何一种解释型代码语言-Groovy,JavaScript,CScript等-但您必须遵循的一条规则是所有业务逻辑都在该代码中!

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

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

我整理了一篇有关这些想法的博客文章:https://medium.com/@thesaadahmad/business-logic-conundrum-offline-mobile-apps-a06ecc134aee