Appearance
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/*.jsonfile: rows of blocks (widgets), plus page-levelmetadata(includingdataSource— 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'smetadata.dataSourcemust match a provider'snameexactly. - 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,searchparams, composite steps) that acore.lookupor 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
- What are the artifact types? — the full folder map
- How does a plugin get installed? — build → upload → pipeline
- How do the SDK modes work? — monorepo vs connected
- How does a page get its data? — the three data paths
- Glossary
- The tutorial builds all of this into one working module.