Business Logic
Business logic in Semantius is data, not code. Rules live on the same entities and fields as your schema, expressed in JsonLogic, and evaluated by a PL/pgSQL engine inside the database itself. There are no hooks to deploy, no functions to host, and no out-of-band scripts to keep in sync with the model. Because enforcement happens at the storage layer, no agent, misbehaving client, or alternate API surface can bypass a rule, every write and every read passes through the same engine.
The platform exposes four extension points. Each one is a column on either an entity or a field, and each one holds a JsonLogic expression (or an array of them):
| Purpose | Column |
|---|---|
| Derives values for the named fields on every insert/update, before the row is written. | entities.computed_fields |
| Each rule must evaluate truthy or the write is rejected with the given message and code. | entities.validation_rules |
| Per-row read filter, only rows where the rule evaluates truthy are visible to the caller. | entities.select_rule |
| Controls field visibility in the UI, evaluated client-side against the current form values to show or hide the field. | fields.input_type_rule |
Why JsonLogic
JsonLogic gives Semantius a single, portable expression language that:
- Serializes cleanly as JSON, so rules sit alongside the rest of the model in the same markdown spec, the same database row, and the same diff.
- Runs identically in the database (for
computed_fields,validation_rules,select_rule) via a PL/pgSQL interpreter and in the browser (forinput_type_rule), with no compiler or runtime to ship. - Cannot be bypassed. Because the engine lives in the database, every insert, update, and select is evaluated against the rules regardless of which client, agent, or service issued the request.
- Is safe to evaluate against untrusted input, since it has no side effects, no I/O, and no host access beyond the variables the platform binds for it.
- Is readable enough that agents can author, audit, and refactor rules without touching application code.
For the language itself, see the JsonLogic overview. For the Semantius-specific variables and operators (such as $user_id, $old, set_record, has_permission, value_changed, throw_error), see JsonLogic extensions.
Where the rules run
The four extension points map onto distinct stages of the request lifecycle:
- Insert or update arrives at the API. The platform loads the entity’s
computed_fieldsand runs them in declared order. Each derived value is written into the incoming row before validation, so later rules can reference it. - Validation runs the entity’s
validation_rulesagainst the merged row. Any rule that evaluates falsy (or that callsthrow_error/require_permissionand fails) rejects the write with the rule’scodeandmessage. - Persist. The row is written.
- Read requests pass through the entity’s
select_rule. The platform evaluates the rule per row, with the row bound as the root context, and returns only rows where it evaluates truthy. This is row-level security expressed as data. - UI render. When a form is built from the model, each field’s
input_type_ruleis evaluated client-side against the current form values. Falsy hides the field, truthy shows it. This is purely a presentation concern and is never the authoritative gate: server-side validation always wins.
Authoring rules
Rules are stored in the model alongside the entities and fields they govern. In day-to-day work you let an agent write and edit them through the Business Analyst skill, and the Deploy skill ships them byte-for-byte to the live instance. When you do need to write one by hand, the workflow is:
- Decide which extension point the rule belongs to. If the rule derives a value, it goes in
computed_fields. If it rejects a write, it goes invalidation_rules. If it filters reads, it goes inselect_rule. If it shows or hides UI, it goes ininput_type_rule. - Write the expression using standard JsonLogic plus the Semantius extensions. Reach for
varto read fields of the current row,$oldto compare against the previous state on update, andset_recordto pull in a related record by id. - Test it with a representative row. The Optimize skill can round-trip a model from a live instance, so you can verify that rules behave as expected in production before locking them in.
Next
- JsonLogic overview: the upstream language, with links and small samples.
- JsonLogic extensions: the variables and operators Semantius adds on top.