Skip to content

Platform overview

A plugin is a bundle of JSON metadata (and optionally a little Java) that the platform's generic engines execute. You rarely write CRUD code — you declare what you want and an engine does it.

App → Module → Page

  • Application — the top-level product surface a user opens (e.g. "HCM").
  • Module — a functional area inside an app (e.g. "Leave Management"). A module owns menu entries.
  • Page — one screen. A metadata/page/*.json file: rows of blocks (widgets), plus page-level metadata (including dataSource — the single field that wires a grid to its data; it is page-level, never grid-level).

Entities

An entity (metadata/entities/*.json, Entity Engine) is a table you declare by its fields and relationships. The platform generates the DDL, the CRUD REST endpoints, grid queries, and single-record reads — zero hand-written persistence code. Mark a foreign-key field with flags.reference.entityType and reads automatically gain a resolved <field>_label sibling (id → display name).

Data providers, data views, data services

  • Data Provider (metadata/provider/*.json) — names a REST base path a grid or lookup reads from. A page's metadata.dataSource must match a provider's name exactly.
  • Data View (metadata/data_view/*.json) — a declared multi-table SQL join over physical tables, read-only.
  • Data Service (metadata/data_service/*.json) — a parameterized query (filters, search params, composite steps) that a core.lookup or a report calls server-side.

Prefer a Data View / Data Service over a bespoke REST endpoint for reads.

Workflows

A workflow (metadata/workflow/*.json, Workflow Engine) is a multi-stage approval process: stagesJson, tasksJson, transitionsJson (each a JSON-encoded string) and a top-level approvalPermissions array that references roles by stable role.code. Every approval-bearing state transition should be a real workflow instance, not a permission-gated direct action.

Rules

An entity rule (metadata/rules/*.json, Rule Engine) fires declarative conditions/actions at lifecycle points (BEFORE_CREATE, AFTER_UPDATE, …) — computed fields, cross-field validation, "on first row, register this job".

Themes

Theming cascades: platform → application → user. A plugin ships theme tokens; it never hardcodes colors in page JSON.

Scheduled jobs

You almost never write a job class. The platform ships generic, JSON-configured sweep engines:

  • status-date-sweep — flip a field when a date column crosses now (± an offset for "N days before" reminders).
  • aggregation-sweep — roll up count/sum/avg/min/max into a summary entity.
  • cadence-generator — create records on a schedule.
  • compliance-sweep — evaluate a boolean expression per row and flag/escalate.

Each is one config row. See erp examples patterns --kind jobs.

Install seeding

To make config rows (job configs, reference data) travel with the .spk instead of a manual per-tenant POST, add metadata/seed-data/*.json: { entity, keyFields, source, rows }. The installer upserts them idempotently on every install/upgrade. Schema: erp schema pull plugin-seed-data.

Go deeper