Skip to main content
Embed the ZEN Engine directly in your application using native SDKs. This deployment model provides the highest performance with no network overhead.

When to use embedded

Choose embedded when you need:
  • Maximum evaluation performance (sub-millisecond latency)
  • Offline capability
  • No external service dependencies
  • Direct control over the engine lifecycle
Consider alternatives when:
  • Multiple services need the same rules
  • You want centralized rule management
  • Business users need to update rules without deployments

Architecture

The engine runs in-process. Decision files are loaded at startup or runtime.

SDKs

Loading decisions

Bundle with your application

Include decision files in your deployment package:
my-app
src
rules
pricing.json
eligibility.json
risk-scoring.json
package.json
Load at application startup:
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import fs from 'fs';
import path from 'path';

const rulesDir = path.join(__dirname, 'rules');

// Precompile decisions into a cache - ZenDecisionContent compiles for better performance
const decisionCache = new Map();
decisionCache.set('pricing.json', new ZenDecisionContent(
  fs.readFileSync(path.join(rulesDir, 'pricing.json'))
));
decisionCache.set('eligibility.json', new ZenDecisionContent(
  fs.readFileSync(path.join(rulesDir, 'eligibility.json'))
));

// Create engine with a loader that returns precompiled decisions
const engine = new ZenEngine({
  loader: async (key) => decisionCache.get(key),
});

// Evaluate by decision name
const result = await engine.evaluate('pricing.json', { customer: { tier: 'gold' } });

Fetch from remote storage

Load decisions from S3, GCS, or HTTP endpoints:
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';

const decisionCache = new Map();

// Loader fetches and precompiles decisions on demand
const loader = async (key) => {
  if (decisionCache.has(key)) {
    return decisionCache.get(key);
  }

  const response = await fetch(`${process.env.RULES_URL}/${key}`);
  const buffer = Buffer.from(await response.arrayBuffer());
  const content = new ZenDecisionContent(buffer);

  decisionCache.set(key, content);
  return content;
};

const engine = new ZenEngine({ loader });

// Evaluate - loader fetches and caches automatically
const result = await engine.evaluate('pricing.json', { customer: { tier: 'gold' } });

Hot reloading

Update decisions without restarting:
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';

class DecisionManager {
  constructor() {
    this.cache = new Map();
    this.engine = new ZenEngine({
      loader: async (key) => this.cache.get(key),
    });
  }

  load(name, buffer) {
    // Precompile and cache
    this.cache.set(name, new ZenDecisionContent(buffer));
  }

  reload(name, buffer) {
    // Atomic swap - precompile new content and replace
    this.cache.set(name, new ZenDecisionContent(buffer));
  }

  async evaluate(name, input) {
    return this.engine.evaluate(name, input);
  }
}

Best practices

Initialize once — Create engine and decisions at startup, not per-request. Handle errors gracefully — Decision loading can fail. Have fallback behavior. Version your rules — Track which rule version is deployed with your application. Test thoroughly — Rule changes ship with code changes. Include rule tests in your CI/CD.

Updating rules

With embedded deployment, updating rules requires redeploying your application:
  1. Export updated rules from BRMS
  2. Add to your repository
  3. Deploy application with new rules
For more dynamic updates, consider the Agent deployment model.