# Modules, forms, virtual tables The three core nouns of xly's metadata model. A reader who internalises these can navigate the rest of the framework. The vocabulary is not always consistent in the codebase — same concept, different names in different files — so part of this page is reconciling the synonyms. ## Module The unit of "a configurable thing in the system". - **One row per module** in [`gdsmodule`](../auto-catalog/tables/gdsmodule.md). - Carries the URL fragment (`sName`), display names (`sChinese`, `sEnglish`, `sBig5`), tree position (`sParentId`, `sAllId`), product edition (`sVersionFlowId`, see [Slice 2](../slices/02-multi-tenancy.md)), optional CRUD procedure names (`sSaveProName`, `sDeleteProName`, `sProcName`, `sCalcProName`, `sSaveProNameBefore`). The Java codebase calls this concept "module", "model", "models", and "modle" (typo, common). The variable `sModelsId` and method `getModelConfigByModleId` both take a *module* `sId`, despite the naming. See the naming caution in [Slice 1](../slices/01-hello-world.md). ## Form The screen layout for a module — header + N field definitions. - **Master:** one row per form in [`gdsconfigformmaster`](../auto-catalog/tables/gdsconfigformmaster.md). Carries the backing object (`sTbName`), backing-object type (`sType` ∈ `{table, view, proc}`), default SQL fragments (`sSqlStr`, `sWhere`, `sOrder`), grid behaviour (`bGrd`, `iPageSize`), jurisdiction column. - **Slave:** one row per *field* in [`gdsconfigformslave`](../auto-catalog/tables/gdsconfigformslave.md). Carries the field name (`sName` — matches a column in the backing object), control type (`sControlName`), display labels, validation rules, default value, dropdown SQL, button instructions. - A module is linked to its form via `gdsconfigformmaster.sParentId = gdsmodule.sId` — semantic FK, no enforcement. (Note: `gdsmodule.sFormId` exists but is empty for most modules and is *not* the canonical link.) The form is the user-visible artefact: when a PM "creates a screen" in BACK, what they're really doing is INSERTing a `gdsconfigformmaster` row plus 5-50 `gdsconfigformslave` rows. ## Virtual table xly's term (and table prefix) for a "table" that is itself defined as metadata, not as a `CREATE TABLE` statement. - One row per virtual table in [`gdsconfigtbmaster`](../auto-catalog/tables/gdsconfigtbmaster.md). - One row per virtual column in [`gdsconfigtbslave`](../auto-catalog/tables/gdsconfigtbslave.md). Virtual tables are how PMs declare "I want this kind of thing" without asking an engineer. Most virtual tables back to a real physical table (the framework supports the migration step, or it pre-creates a generic "wide" table the PM populates), but the *abstraction* the runtime sees is the metadata declaration, not the underlying storage. This is distinct from a database **view**: a virtual table is metadata; a view is a `CREATE VIEW` SQL object. Both can back a form (via `gdsconfigformmaster.sType = 'table'` or `'view'`). See [Slice 3](../slices/03-report.md) for the view-backed case. ## How they fit together ``` gdsmodule (module, 1 row) └── (joined via sParentId) gdsconfigformmaster (form-master, 1 row per form) ├── gdsconfigformslave (fields, N rows) ├── gdsconfigformcustomslave (per-tenant fields, N or 0) └── gdsconfigformuserslave (per-user tweaks, N or 0) gdsmodule.sName → registered route in gdsroute (URL whitelist) gdsmodule.sFormId → (mostly empty — historical) ``` Every form has exactly one module. Every module *should* have at most one form, but a few have several `gdsconfigformmaster` rows pointing at the same `sParentId` — those represent screens with multiple panels or sub-tabs. ## Three nouns, one engine The runtime — `BusinessBaseController` and `BusinessBaseServiceImpl`, documented in [Slice 1](../slices/01-hello-world.md) — knows how to render any module / form / virtual-table combination. There is no per-module Java code. PMs creating new modules are creating new rows; they are not creating new code paths.