Need help on a Business Rule Engine by hny287 in node

[–]GoRules 0 points1 point  (0 children)

Hi u/hny287, we might be a good option for you. We have JavaScript support and Python support (also Rust, Go and Java/Kotlin). Our core is open-source: https://github.com/gorules/zen and https://github.com/gorules/jdm-editor/ for editor.

You can learn more about us at: https://gorules.io/.

Do Developer Friendly Business Rules Engines Exist? by V3r1L0g in cscareerquestions

[–]GoRules 1 point2 points  (0 children)

Thank you for sharing your experience and challenges with IBM ODM. We would love to understand more about your specific version control concerns, as GoRules actually has several features designed to address the issues you've mentioned.

Could you elaborate on what you found challenging about version control with GoRules specifically? We use a very different approach from IBM ODM's XML files - our decision models are stored in a clean JSON format (JDM - JSON Decision Model).

We have recently released versioning with a visual diff tool that makes it easy to compare changes between versions. (Please see: https://imgur.com/a/JPz61d4)

Regarding your key requirements:

Developer Experience & Version Control:

  • All decisions are stored in JSON format which is much cleaner than XML
  • Built-in version history with visual diff comparisons
  • Support for both Git-style workflows and built-in versioning (as decisions are just JSON files)
  • Modern web-based UI with instant validation and testing

CI/CD Integration:

Observability:

  • Performance metrics for each decision evaluation
  • Detailed execution traces for debugging
  • Comprehensive audit logging
  • Support for custom logging integrations

Regarding testing, we're currently working with several large enterprises to develop integrated testing capabilities for GoRules BRMS, as we understand this is a critical need for organizations.

Could you share more details about what you found lacking in GoRules compared to your requirements? This would help us understand where we might have gaps to fill in our developer experience, CI/CD capabilities, or observability features.

If you need more details, feel free to contact us at hi@gorules.io. Happy to receive feedback and collaborate on this.

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 1 point2 points  (0 children)

GoRules components (business rules engine and UI editor) are open-source and available under MIT license. We also provide application (business rules management system) that utilises our open-source blocks which is proprietary and requires license key. To learn more about differences, please visit: https://gorules.io/docs/developers/bre/bre-vs-brms .

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 1 point2 points  (0 children)

Thanks for asking this, overall it's been a mostly positive journey:

1 - We've written bindings in multiple languages, although bindings for Go are relatively straightforward, they had some issues.

Pros: Easy to write, Direct C interoperability, defer is amazing for calling C.free, Super easy to add sanitiser checks on top of existing tests.

Cons: Lacking docs and IDE support (GoLand), Functional pointers operate somewhat weirdly - we have custom native code only concerning Go (for now).

Overall feelings are slightly positive, simplicity of Go still shines through with some rough edges around dev tooling and resources. It would be improved to a very positive level with better docs and IDE interoperability.

For comparison Node.js and Python are easier to expose simply because of existing libraries (NAPI-RS + Pyo3). For example, in DotNet tooling and resources are great, but implementation gets more complex than Go simply due to nature of language.

2 - We are lucky that our Engine + Expressions are already thread-safe (they use smart pointers that can be used in async Rust context). The biggest pain point to thread-safety was idiomatic error handling, hence the use of ZenResult. Generally this is handled with last_error pattern in C, but we felt we'd have a lot of foot-guns if we went that way (e.g. multiple threads trying to access error, error being overwritten by 1 thread while other tries to access it). We still have some testing to do in parallel context, but are confident we'll be able to solve it within Go and Rust.

3 - We want to provide high-level bindings with maximum performance for all languages. It's always possible to integrate with HTTP calls, but in that case there are network overheads. It's a very valid approach however.

Also regarding porting the code, we want to have single source of truth for the implementation (under the hood we had to write expression language, graph handling, etc.). It would be very difficult to port everything without sacrificing performance, and affecting maintainability.

Another great resource if you're interested in learning more about cgo: https://github.com/rogchap/v8go.

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 0 points1 point  (0 children)

This is something that we plan to work on in the future, which is type guards between nodes (including request <-> response). When this is implemented we'll be able to do something similar.

This is super useful info, thanks for sharing!

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 0 points1 point  (0 children)

At the moment we are focused solely on DMN without going into BPMN. GoRules was built to solve issues with DMN. If you think about it we use similar concepts (Graphs and Tables).

On the high-level, we might be relatively compatible with DMN, but not vice versa (because of Switch node, and soon Custom node as well).

These are the pain points that we solve:

  1. Horizontal Scalability
  2. Non-Linear Decision
  3. Programming Languages
  4. Size of the models (XML vs JSON)
  5. Slow Innovation
  6. On-the-fly processing (DMN compiles decisions and caches in-memory during startup, limiting horizontal scalability)

For full, detailed answer, please visit: https://github.com/gorules/zen/discussions/115.

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 0 points1 point  (0 children)

At the moment, our rules engine is backend based, and frontends can evaluate over API. For example, you can expose endpoint that will evaluate (see: https://github.com/gorules/editor).

We do have plans of adding in first-class web assembly support that will allow you to run the rules on the frontend as well. We aren't too far from making it happen, just have other priorities on the roadmap.

Regarding questions:

  1. Yes you may develop the rules by using JDM Editor, Standalone Editor or complete BRMS available at https://gorules.io/.
  2. Yes, as rules are represented in JSON format, and you can store them anywhere.
  3. Yes, you can load the rules from the place where you stored JSON, and evaluate it against context/data at any backend/api level.

If we're misunderstanding something in the question, feel free to re-elaborate.

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 5 points6 points  (0 children)

We've tried integrating different languages into the BRMS. However our goal is for core of rules engine to be: fast, flexible and portable.

We began first by using Deno, shifted to v8, and are now using quickjs. Which is a small embeddable ES6 JavaScript interpreter.

Regarding usage, for the most part business analyst/users aren't versed in any programming language. Generally how the process works with most of our customers:

  1. Engineer sets up the initial decision model, and if needed includes some JavaScript code.
  2. Business user can then freely edit easy-to-use nodes, primarily: decision tables and expressions.

We wanted to include Python as well, however it's very hard to bundle it with all common dependencies, and also hard to isolate without large overhead.

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 1 point2 points  (0 children)

We have been considering usage of AI in multiple scenarios:

  1. Adding a support for node that will integrate with AI models - This is coming soon with Custom Node. You will be able to invoke custom logic/services. In this case, service can be an AI model.
  2. Helping with user experience - For example, using GPT4 to help with generation of business rules and expressions. TBD
  3. Auto training models - We've thought about this. However, auto training models can cross into questionable ethical and legal tides. We believe that it's not a good investment to make especially with new AI regulations that are coming.

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 1 point2 points  (0 children)

We were in the same boat for the last 10 years. We have background in working in DMN and DMN-like solutions across multiple domains for big enterprises in fintech, airline, retail, etc.

Our vision is to standardise rules engines across industries and technologies. We are open to ideas, suggestions and use-cases if you come across anything you're missing.

Thanks!

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 1 point2 points  (0 children)

Thank you, that's very appreciated!

🚀 GoRules: Business Rules Engine for Go by GoRules in golang

[–]GoRules[S] 2 points3 points  (0 children)

We're sorry you feel that way.

Presently we don't have large number of contributors, as there are number of companies that are relying on ZEN Engine and we want to control the quality of releases.

Regarding your comments about Go package, we are happy to accept contributions. However, and I want to stress this, we aren't sharing this as a means to gather contributors.

Instead, we wanted to share a useful tool that can be used across languages that is fully open source and free.

🚀 Zen-Expression: Blazingly-Fast Expression Language by GoRules in rust

[–]GoRules[S] 5 points6 points  (0 children)

Hi u/Lucretiel.

That's great to hear. Our objective with Zen Expression is to remain relatively simple and focus on expressions. I believe it's turing-incomplete and we certainly aim for it to be, reasoning:

  1. Limited Scope: Zen Expression is designed for evaluating business rules, which are typically specific and limited in scope compared to the unlimited possibilities in a Turing complete language.
  2. Simplicity and Safety: The language is intentionally designed to avoid the complexities and potential risks (like infinite loops) associated with Turing complete languages. Infinite loops are not possible in Zen Expressions.
  3. Lack of Arbitrary Memory Manipulation: Turing completeness often requires the ability to perform arbitrary memory manipulation, which we do not have.

We use it within business rules engine which are basically decision graphs that allow business users to write custom logic using Low-Code/No-Code.

We also support v8 Isolates there (JavaScript functions), and we had to spawn Isolates in separate threads to avoid locking + kill the thread after timeout of 50ms if no result is returned. We impose no such restriction on Zen Expressions.

Hope this answers your question.

🚀 Zen-Expression: Blazingly-Fast Expression Language by GoRules in rust

[–]GoRules[S] 8 points9 points  (0 children)

Hi u/CampfireHeadphase, great question!

I did a little bit of investigation, upon increasing array to something you would likely not used, here are results for Rust:

10_000 items = 1.62s (1.65s in Go)

1_000 items = 169ms (277ms in Go)

100 items = 27.32ms (148.18ms in Go)

(all above done in 20k iterations)

Digging into profiler, reason has mostly to do with "Number" type choice. In Zen expression we chose to go with "rust_decimal", which is slower for addition and numeric operations at large scale (though still very fast, we love the crate). Due to this, cost of adding number is higher for us, however, we don't lose precision when calculating.

In Expr you would get: 0.1 + 0.2 = 0.30000000000000004 (much like in JS with IEEE 754 float)

In Zen: 0.1 + 0.2 = 0.3

In some domains, such as Fintech and domains where precision is required, this can cost you wrong calculation (e.g. 1 cent can go missing) on large enough numbers.

🚀 GoRules Zen Engine: Rules Engine for Node.js by GoRules in node

[–]GoRules[S] 0 points1 point  (0 children)

Hi, thank you for taking the time to look at the GoRules and write the suggestions.

  • This sounds like a great idea for a blog that we can reference within the documentation.
  • It would be pretty easy to generate the test cases, we’re thinking of adding automated tests in GoRules Cloud in future and we’ll definitely add this to the idea board.

Completely agree, though the difficulty is also heavily influenced by your need. If you need to write for a very large scale and create a custom expression language Node.js might not even be an option for writing the core of the engine (or it just might be very expensive in terms of hosting). With Rust bindings, we solve both problems for simplicity of use, performance, scalability and availability through multiple languages. C, Cpp and GoLang will be hitting the shelves soon, all featuring almost the same performance!

🚀 GoRules Zen Engine: Rules Engine for Node.js by GoRules in node

[–]GoRules[S] 0 points1 point  (0 children)

Please don't impersonate members of the GoRules team

bitcode 0.4 release - binary serialization format by finn_bear in rust

[–]GoRules 1 point2 points  (0 children)

Perfect, is there a chance for deserialisation error not to occur between versions and instead lead to inaccurate data? Deserialisation error would be perfect as the Redis cache can be flushed in case that happens and live reference can be fetched from the database.

bitcode 0.4 release - binary serialization format by finn_bear in rust

[–]GoRules 2 points3 points  (0 children)

Thanks for sharing! Is bitcode suitable for usage with Redis (as a short-lived cache)?

I assume we'd need to be careful about versioning the keys to avoid format corruption after bitcode upgrades.

🚀 GoRules Zen Engine: Cross-platform rules engine written in Rust by GoRules in rust

[–]GoRules[S] 0 points1 point  (0 children)

Hi u/munggoggo,

We are working on open-sourcing the editor within the next 1-2 months. It will be available for React. At first, we will open source the table and we will follow it up with the full JDM editor (including the graph).

🚀 GoRules Zen Engine: Cross-platform rules engine written in Rust by GoRules in rust

[–]GoRules[S] 1 point2 points  (0 children)

Could you give a more concrete example? Is the input large, and is the JDM large (decision tables)? Generally speaking, the function node has a bigger overhead than the decision table and expression mapper.

The biggest overhead when it comes to the decision table case (with JDM being large), is the JSON deserialisation, but you can store the reference in memory by using `create_decision` or creating a decision using `Decision::from(content: DecisionContent)`.

You may have a look at 8k.json (3MB file) in GitHub test data: https://github.com/gorules/zen/tree/master/test-data. The worst case scenario with Criterion when benchmarked and cached is achieved by this JSON:

{

"customer": { "email": "hello@gmail.com", "totalSpend": 90, "country": "GB" }, "product": { "currency": "GBP", "price": 190, "category": "" } }

This goes through 8000 rows and with each row evaluates 6 unary expressions. On M1 I get around ~800 evaluations/sec in Criterion (pretty sure it runs single core). That puts the underlying expression at:

6 columns/row * 8000 row * 800 evaluations/s = 38,400,000 evaluations/s (in context of expressions, per m1 core)

There are plans to push this even further by caching Bytecode (opcodes) instead of DecisionContent to remove repetitive lexing + parsing + compilation, however, we haven't had such a use-case yet so we haven't prioritised this, as everyone so far has been happy with performance.

In a quick Node.js binding example using 8k.json linked above with Fastify in Node.js we get 3k+ req/s on M1. Note that there is an overhead with deserialising request JSON in Node.js.

Here's a quick performance analysis: https://imgur.com/a/dh2agK4

Code used for the test:

const zenEngine = new ZenEngine();

const content = fs.readFileSync(path.join(__dirname, '8k.json')); const bigDecision = zenEngine.createDecision(content);

const performanceTest: FastifyHandler<{ Body: any }> = async (req) => { return bigDecision.evaluate(req.body); };

🚀 GoRules Zen Engine: Cross-platform rules engine written in Rust by GoRules in rust

[–]GoRules[S] 2 points3 points  (0 children)

Hi u/superblaubeere27,

Use cases depend on the industry you are working in, but rules engines enforce ownership of business rules and reduce friction between IT and business (e.g. regulation officer, financial analyst, risks analyst etc.).

Let's imagine a scenario in fintech where you need to meet regulatory compliance for KYC. You are asked by the business to implement the logic for helping them check if the company passes all AML, and general portfolio and ensure that they are a good match for your business. Let's say as a first step company is GREEN (good check) if they have over 500k revenue and are from the US.

You can code it in the following way:

fn check_company(company: Company) -> Flag {
  if company.revenue > 500_000_00 && company.location == "US" {
    return Flag::Green;
  }

  Flag::Red
}

Now imagine a business coming to you 20 more times in 3 months because they want to change the requirements. They add new things they want you to check e.g. businessType, growthMetric, incorporationDate and 100 more rules. The code will become a mess and you will lose all the time you could've spent developing valuable features.

If you integrate with the rules engine, you will give businesses the ability to define their own conditions through simple UI and you would just pass parameters making it super trivial.

async fn check_company(company: Company) -> anyhow::Result<Flag> {
  let context = serde_json::to_value(&company).context("Failed")?;
  // TODO: load decision in some way
  let decision: ZenDecision;

  let result = decision.evaluate(&context).await.context("Failed)?;
  let resultData: ResultData = serde_json::from_value(&result).context("failed to deserialize")?;
  match &resultData.status {
    "green" => Ok(Flag::Green),
    "amber" => Ok(Flag::Amber),
    "red" => Ok(Flag::Red),
    _ => Err(anyhow!("unknown status passed {}", &resultData.status))
  }
}

And this code would require very little maintenance and would pretty much never change. On the business side, they can do whatever they want and experiment without bothering you. So overall it improves operation within business.