TinyRuleKit

Expression Views

Expression views define what rule expressions can read from your domain model. They are the main boundary between configurable rule text and application objects.

Safe Expression Views

Do not expose mutable domain objects directly unless they are intentionally safe. Objects can implement ExpressionAccessible to return a restricted view for expressions.

JAVA
record PlayerView(String name, int level, Set<String> permissions) {
    boolean hasPermission(String permission) {
        return permissions.contains(permission);
    }
}

final class Player implements ExpressionAccessible {
    private final String name;
    private final int level;
    private final Set<String> permissions;
    private final PlayerView expressionView;

    Player(String name, int level, Set<String> permissions) {
        this.name = name;
        this.level = level;
        this.permissions = Set.copyOf(permissions);
        this.expressionView = new PlayerView(name, level, this.permissions);
    }

    @Override
    public Object toExpressionView() {
        return expressionView;
    }
}

When this object is placed in variables, TinyRuleKit stores the expression view:

JAVA
RuleVariables.create()
        .put("player", player);

Expressions can then use the safe view:

JSON
{
  "type": "expr",
  "expr": "player.level() >= 10 && player.hasPermission('vip')"
}

Safe Self Views

Immutable objects that are intentionally safe can expose themselves:

JAVA
@ExpressionAccess(viewMode = ExpressionViewMode.SELF)
record PlayerView(String name, int level) implements ExpressionAccessible {
    @Override
    public boolean isExpressionSafe() {
        return true;
    }

    @Override
    public Object toExpressionView() {
        return this;
    }
}

If an object returns itself without declaring safety, TinyRuleKit rejects it during precompilation.

Scalar Variables

Simple values do not need an expression view, but they must be added explicitly:

JAVA
RuleVariables.create()
        .putScalar("level", 10)
        .putScalar("permission", "vip")
        .putScalar("enabled", true);

putScalar accepts primitive wrapper values and strings. Collections, domain objects, services, repositories, and other objects must use a safe expression view instead.

Recommended View Shape

  • expose read-only data, not mutable domain objects
  • prefer primitive values, strings, records, enums, and immutable collections inside views
  • keep methods side-effect free
  • expose business concepts, not infrastructure objects
  • avoid repositories, file APIs, threads, classloaders, reflection, and runtime execution

Cached Views

Expressions are compiled once when the engine is built, but expression views may be created every time ruleVariables() is called. Prefer cached views in hot paths:

JAVA
final class Wallet implements ExpressionAccessible {
    private int coins;
    private final WalletView expressionView = new WalletView(this);

    @Override
    public Object toExpressionView() {
        return expressionView;
    }

    static final class WalletView {
        private final Wallet wallet;

        WalletView(Wallet wallet) {
            this.wallet = wallet;
        }

        public int coins() {
            return wallet.coins;
        }
    }
}

Creating a new view on every access is allowed, but TinyRuleKit warns during precompilation so you can avoid unnecessary allocations in hot paths.

Continue with Expression Best Practices before configuring expression security.