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

31
随着Web应用程序需求的增长,我发现自己编写更多基于API的Web应用程序。我使用像AngularJS这样的框架构建与这些API进行通信的丰富Web客户端。目前,我在服务器端/API方面使用PHP(Lumen或Laravel)。
问题是,我经常发现自己在客户端和服务器端之间重复业务逻辑。
当我说业务逻辑时,我的意思是针对订单表单等规则,例如:
- 如果您购买了Y,您可以购买X。 - 如果您有Z,您不能购买Y。 - 如果您购买10个,您可以获得10%的折扣。 - 高度×宽度×深度×成本=最终成本。 - 如果宽度大于5,则高度必须在10到20之间。 - 等等。
为了使此应用程序既响应迅速又快速,计算逻辑(以及其他业务逻辑)正在客户端上进行。由于我们不应该信任客户端,因此我会在服务器端重新验证这些数字。这种逻辑可能会变得非常复杂,并且在两个位置编写此复杂的逻辑会感觉很危险。
我有三种解决方案:
1. 使需要业务逻辑的所有内容都向API发出AJAX调用。所有业务逻辑都将在一个地方中运行,并且只需测试一次。这可能会很慢,因为客户端必须等待每个订单表单更改以获取更新的值和结果。拥有非常快速的API将有助于解决此问题。主要的缺点是当用户网络连接差(例如移动设备)时,这种方法可能无法良好工作。
2. 在客户端和服务器端上编写业务逻辑。客户端可以即时获得反馈并对表单进行更改,我们可以在提交到服务器时验证所有数据。这里的缺点是我们必须复制所有业务逻辑并测试两个方面。这肯定需要更多的工作,未来的任务会变得不稳定。
3.
  • 相信客户端!?!在客户端编写所有业务逻辑并假设他们没有篡改数据。在我的当前场景中,我正在开发一个报价生成器,该生成器将始终由人工审核,因此这可能是可以接受的。

  • 说实话,我对任何解决方案都不太满意,这就是为什么我向社区寻求建议的原因。我很想听听您对这个问题的看法或方法!


    在服务器上使用PHP的ajax是最酷的方法,不应该超过几毫秒,如果需要更长时间,您还可以设置加载屏幕或警告。您可以转移到像meteor/node这样的框架,一次编写整个代码,并仅为服务器或客户端明确一些细节。您可以在HTML表单上进行基本验证,然后在服务器上进行大型验证。您可以在js中创建库,使其可从客户端访问并从服务器访问。请查看此http://php.net/manual/en/v8js.executestring.php - Cristo
    也许你会对我找到的另一个教程感兴趣:http://www.phpied.com/server-side-react-with-php/ - Cristo
    也许你可以使用websocket?它非常快速,并且可以使用javascript。我真的不知道它是否使用了很多带宽。你可以考虑在需要时连接到这个websocket,完成交易后再断开连接。Websockets非常有趣,而且非常容易实现。看看这个:http://socket.io/。 - Thomas Van der Veen
    1
    我会同时使用服务器端和客户端验证;但我不喜欢重复代码的想法,所以最有可能的是我会让服务器传递验证规则到客户端,然后让客户端使用这些规则,这样就可以编写一个函数来验证所有情况(所有更改都将从服务器端进行,因此无论客户端进行了什么更改,它仍然能够正确地验证)。然后在服务器端(最终提交)保存之前进行验证。 - ahmad
    使用代码生成来避免重复逻辑(或者说是努力)。使用服务器端组件进行服务器验证,并让它们发出HTML和JS(模板、验证代码等)以供客户端使用。 - nothingisnecessary
    显示剩余2条评论
    9个回答

    9
    你还可以做一件事情。
    使用JavaScript编写您的验证和业务逻辑代码。但是尽可能地将其松耦合。如果可能,只需以JSON作为输入并输出JSON即可。
    然后,在现有的PHP服务器旁边设置一个独立的NodeJS服务器,将该逻辑服务于客户端,以便在客户端上可以在不需要AJAX调用的情况下使用它。
    然后在PHP服务器上,当您需要验证和运行所有这些业务逻辑规则时,使用cURL调用NodeJS业务逻辑并验证数据。这意味着从PHP服务器到NodeJS服务器的HTTP调用。 NodeJS服务器将具有其他代码,该代码将接收数据,使用相同的代码进行验证,并返回结果。
    通过这种方式,您可以实现以下目标:
    1.更快的开发-一个地方对您的逻辑进行单元测试。
    2.更快的客户端代码执行-由于NodeJS向客户端提供了相同的验证JavaScript代码,因此不需要AJAX。
    3.所有业务逻辑都存储在NodeJS服务器中-当业务逻辑发生更改时,您只需要触及此部分;因此,在不久的将来,如果您需要创建其他附加接口,那么您可以使用此服务器验证您的数据。它将像您的业务规则服务器一样工作。
    您唯一需要做的事情就是在PHP服务器旁边设置一个NodeJS服务器。但是您不需要更改所有代码,以在NodeJS服务器上运行。

    2
    或者一开始就在Node中完成所有服务器端开发。 - Mike Feltman

    8
    当我决定使用Laravel作为后端,Angular 2作为前端创建应用程序时,我遇到了同样的问题。在我看来,目前并没有避免业务逻辑重复的解决方案,因为:
    目前PHP和JavaScript不能相互转换。如果我们可以使用相同的语言编写业务逻辑,然后将它们嵌入到后端和前端中,那会很好。从这一点出发,我又想到了另一个问题:
    为了实现这个目标,我们应该只在一种语言中编写业务逻辑,目前JavaScript是最好的解决方案。正如你所知,TypeScript / EMCA Script帮助我们以面向对象的方式编写代码。Meteor框架NodeJS基础设施帮助我们使用JavaScript在后端和前端运行。
    因此,从我的角度来看,我们可以使用TypeScript / EMCA来编写业务逻辑的包,例如,在JavaScript中编写的验证类可以在两侧实现,因此您只需编写一次,但是它也将从前端和后端调用两次。
    这就是我的意见。希望看到其他关于这个非常有趣的主题的解决方案。

    这个需要注意的是,它需要进行语言更改(从php到nodejs)。看起来这可能是唯一的方法,没有任何重复的情况下完成此操作 :( - Roeland
    你可以在纯JavaScript中使用业务逻辑,并将其部署在Node.js上。您可以将其余的代码放置在PHP服务器上。通过这种方式,您无需将整个代码切换到Node.js上,可以节省重复工作。请看我的答案。 - Partha Sarathi Ghosh

    7
    一种可能的解决方案是使用XML或JSON Schema等声明性抽象语言来声明您的验证规则。然后在客户端,比如AngularJS——您可以将这些规则转换成一个现成的表单渲染器。因此,在客户端,您最终会得到能够验证已声明规则的表单。
    接下来,在您的服务器端API上,您需要创建一个可重复使用的验证引擎,该引擎将根据定义的规则进行验证。您最终将得到一个单一的地方,即您的JSON Schema或任何其他您声明定义规则的地方,其中定义了您的表单和验证规则。

    4
    当我在自己的项目上工作时,我也曾处于这种境地。利用客户端设备的功能进行重要操作并在服务器端仅验证结果总是很有诱惑力。这将导致业务逻辑出现两次,即前端和后端。
    我认为选项1是最佳选择,它是最合理和最符合逻辑的。如果您将来想扩展Web应用程序到原生移动应用程序中,您将能够通过调用这些API重用所有业务逻辑。对我来说,这是一个巨大的胜利。
    如果担心过多的API请求会影响移动性能,则可以尝试将一些请求分组并在最后执行单个检查?因此,不要针对表单中的每个字段进行检查,而是在用户提交整个表单时进行检查。此外,只要将请求和响应数据保持到最小,大多数Internet连接就足够了,所以不用担心这个问题。
    我通常遇到的更大问题是,由于您的Web应用程序将被分解为各个部分,并调用相关的API。应用程序的状态更加复杂,因为用户可以在这些状态之间跳转。您需要非常谨慎地考虑用户旅程,并确保该过程没有错误。
    以下是我必须处理的一些常见问题:
    - 如果API返回错误,前端是否显示错误? - 如果用户犯了一个错误并提交了表单,则应该看到一个错误。但是一旦用户纠正错误并再次提交,错误应该隐藏,成功消息现在应该显示。 - 如果API存在错误或Internet连接不稳定,因此没有返回任何内容。前端会挂起吗? - 如果有多个错误消息,前端能否/是否会将它们全部显示?
    我建议在前端进行大量的单元测试,以确保其稳定性,即使业务逻辑仅在后端处理。

    2
    首先:永远不要相信客户端。
    话虽如此,我经常处理此类问题,但遗憾的是我没有找到简单的解决方案。您需要在两个方面进行验证,但是您不需要在这两个方面上进行整个验证。
    我尝试平衡它。在客户端上,您可以进行大部分简单(但有价值的)验证,正常的内容,数字必须是数字,日期必须是日期,数据在范围内等等,因此当您提交它时,它会发送到服务器进行完全验证,但是您确保在客户端上,大部分信息至少是以正确的格式,并且某些(或大部分)已经验证过,然而,真正的业务逻辑是在服务器端完成的,但是由于大多数数据已经正确,因此服务器端验证很可能会批准请求,因此您将避免很多重新提交。
    现在,如何使其更改时您不需要在两个方面进行更改?好吧,有时您无法避免此问题,当需要进行重大更改时,但是业务逻辑参数可以共享,并且像您建议的那样,这可以通过ajax完成。您制作一个php文件,在其中拥有所有业务逻辑参数,并使用ajax请求在客户端加载它,仅一次(在加载脚本时),您需要对此进行优化,以便仅获取参数值,其他所有内容应已经存在于客户端,因此,如果业务逻辑中的某个参数值更改,则只需在参数文件中更改即可。 (如果脚本加载后更改了参数,则验证将在服务器端失败,现在您需要决定是否强制它们重新加载脚本,以便重新加载参数,或者不是,我让他们重新加载)
    我想您已经明白了。这就是我所做的,并且对我来说效果还不错,可以节省很多重编码时间。
    希望您会发现此信息有用。

    0

    我认为在未来,选项1是最好的选择。API优先开发可以确保所有业务逻辑得到测试并正常工作,并允许接口进行访问。你永远都不应该信任用户!

    与为每个所需接口编写相同逻辑相比,API优先开发具有无限的潜力。


    0

    这里有一个关于将逻辑放在客户端还是服务器端的类似讨论。最终,每种情况都是独特的,需要不同的计划,但是这个帖子中有一些很好的指导性建议。

    客户端 vs. 服务器端


    0

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

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

    今天,这项技术已经得到了大多数现代浏览器的支持,但仅限于c/c++。因此,如果您具备这些技能,就可以立即使用它。

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

    这只是我的个人意见,但是 => 在 NodeJS 中编写逻辑是重用 JavaScript 代码的解决方案,但是当涉及到大型可维护逻辑代码时,我仍然感觉需要一种强类型语言。 (是的,我知道 TypeScript 很好,但我想要更多)。WebAssembly 还年轻,但肯定会带来大量改进以遵守 DRY 原则。


    0

    非常有趣的问题 - 另一个注意事项是我们希望支持离线模式,即应用程序也必须在离线状态下运行。

    另一个进一步的复杂性将是,假设您很好的服务器端全部采用一种技术,例如Java或.Net等,而在客户端,您可以选择原生工具或Xamarin之类的东西,但不幸的是与服务器不同。

    因此,Partha的方法似乎最有前途 - 但正如所述,这在完全离线模式下不起作用。因此,稍微修改的方法将是将验证规则视为数据。但不是简单的数据 - 而是说“整个该死的代码都是数据”。您可以选择任何解释代码语言,例如Groovy、JavaScript、CScript等,但您将100%遵循一个规则,即所有业务逻辑都在该代码中!

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

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

    我已经撰写了一篇博客文章,介绍了这些想法:https://medium.com/@thesaadahmad/business-logic-conundrum-offline-mobile-apps-a06ecc134aee


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