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

use*_*892 4 node.js express angularjs mean-stack

我正在创建一个 MEAN 堆栈,我想澄清以下内容。

从编码标准的角度来看,我知道验证应该在客户端和服务器端执行。我想要实现的是执行完全相同的验证代码,这样我就不会再次重复该代码。这更像是客户端和服务器端的共享代码。

那么如何让 Angular js 和 Express js 调用相同的 .js 文件来执行验证呢?有可能吗?

谢谢!

Lud*_*c C 5

你一定能做到这一点。RemObjects DataAbstract ( http://old.wiki.remobjects.com/wiki/Business_Rules_Scripting_API )使用此方法。这里的原则是定义适用于客户端和服务器或仅适用于服务器的业务规则。您几乎永远不必仅在客户端上检查业务规则,因为您永远不能“信任”客户端来检查您的业务规则。

CQRS 和 DDD 是两个可以为您提供帮助的架构原则。领域驱动设计将“清理”或“精炼”您的代码,使基础设施远离核心“领域”逻辑。而且业务规则仅适用于域,因此最好将域与其他域隔离。

命令-查询-责任-分离。我很喜欢这个。基本上,您定义了一组命令,这些命令将在应用之前进行验证。不再有类似 Model.Set('a', 2) 的类似机器的代码。使用此原则,您的代码将类似于 MyUnderstandableBusinessObject.UnderstandableCommand(aFriendlyArgument)。在应用业务规则时,您的实际命令反映了您的域的用例,这非常方便。

当我从事node.js / javascript项目时,我也总是遇到这个问题。问题是你没有一个客户端和服务器都能理解的标准化 ORM。这是自相矛盾的,因为 Node.js 和浏览器运行在同一种语言上。当我被 Node.js 吸引时,我告诉自己,客户端和服务器都运行相同的语言,这将节省很多时间。但这有点错误,因为即使 npm 确实很活跃,也没有那么多成熟和专业的工具。

我也想构建一个客户端/服务器都能理解的 ORM,并为其添加关系方面(以便它与 SQL 兼容),但我有点放弃了该项目。https://github.com/ludydoo/affinity

但是,还有其他一些解决方案。Backbone就是其中之一,而且它很轻量。

您必须在此处实际执行业务规则检查。您需要将模型中的“验证”部分提取到另一个可以共享的对象中。一些可以帮助您入门的东西:

https://jsfiddle.net/ludydoo/y0otcvrf/

BusinessRuleRepository = function() {
  this.rules = [];
}

BusinessRuleRepository.prototype.addRule = function(aModelClass, operation, callback) {
  this.rules.push({
    class: aModelClass,
    operation: operation,
    callback: callback
  })
}

BusinessRuleRepository.prototype.validate = function(object, operation, args) {
  _.forIn(this.rules, function(rule) {
    if (object.constructor == rule.class && operation == rule.operation) {
      rule.callback(object, args)
    }
  })

}

MyObject = function() {
  this.a = 2;
}
MyObject.prototype.setA = function(value) {
  aBusinessRuleRepo.validate(this, 'setA', arguments);
  this.a = value;
}


// Creating the repository
var aBusinessRuleRepo = new BusinessRuleRepository();

//-------------------------------
// shared.js
var shared = function(aRepository) {
  aRepository.addRule(MyObject, 'setA', function(object, args) {
    if (args[0] < 0) {
      throw 'Invalid value A';
    }
  })
}
if (aBusinessRuleRepo != undefined) {
  shared(aBusinessRuleRepo);
}
//-------------------------------


// Creating the object

var aObject = new MyObject();

try {
  aObject.setA(-1); // throws
} catch (err) {
  alert('Shared Error : ' + err);
}

aObject.setA(2);



//-------------------------------
// server.js
var server = function(aRepository) {
  aRepository.addRule(MyObject, 'setA', function(object, args) {
    if (args[0] > 100) {
      throw 'Invalid value A';
    }
  })
}
if (aBusinessRuleRepo != undefined) {
  server(aBusinessRuleRepo);
}
//-------------------------------
// on server


try {
  aObject.setA(200); // throws
} catch (err) {
  alert('Server Error :' + err);
}
Run Code Online (Sandbox Code Playgroud)
  • 首先是建模和定义您的领域。您将拥有一组代表您的业务对象的类,以及对应于业务操作的方法。(对于你的情况,我真的会选择 CQRS)

  • 模型定义将在客户端和服务器之间共享。

  • 您必须定义两个文件或两个对象。分开了。服务器规则和共享规则。这些将是一组 Repository.addRule() 调用,它们将在存储库中注册您的业务规则。您的客户端将获得 Shared.js 业务规则,服务器将获得 Shared.js + Server.js 业务规则。这些业务规则将始终以这种方式应用于您的对象。

我向您展示的代码小示例非常简单,仅在应用命令之前检查业务规则。也许您可以添加参数“beforeCommand”和“afterCommand”来检查更改前后的业务规则。然后,如果您添加在应用命令后检查业务规则的可能性,您必须能够回滚更改(我认为主干具有此功能)。

祝你好运


您可以通过自动获取您所在方法的名称来自动执行此操作(我可以获取 JavaScript 中当前正在运行的函数的名称吗?

function checkBusinessRules(model, arguments){
   businessRuleRepo.validate(model, getCalleeName, arguments);
}

Model.prototype.command = function(arg){
   checkBusinessRules(this, arguments);
   // perform logic
}
Run Code Online (Sandbox Code Playgroud)

编辑2

我想纠正我的第一个答案的一个小细节。不要将您的业务规则应用于财产设置者!请改用业务运营名称:

您必须确保始终通过方法设置模型属性。如果您直接通过分配值来设置模型属性,则您将绕过整个业务规则处理器。

廉价的方法是通过标准制定者来做到这一点,例如

SetMyProperty(value);
SetAnotherProperty(value);
Run Code Online (Sandbox Code Playgroud)

这是一种低级业务规则逻辑(基于 getter 和 setter)。那么,你的业务规则也会是低级的。这有点不好。

更好的是,您应该通过业务可理解的高级方法名称来完成此操作,例如

RegisterClient(client);
InvalidateMandate(mandate);
Run Code Online (Sandbox Code Playgroud)

然后,您的业务规则将变得更容易理解,并且您几乎会很高兴地实施它们。

BusinessRuleRepository.add(ModelClass, "RegisterClient", function(){
    if (!Session.can('RegisterClient')) { fail('Unauthorized'); }
})
Run Code Online (Sandbox Code Playgroud)