# 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) — handles every module / form / virtual-table combination through one universal dispatch path. There is no per-module Java; PMs creating new modules are creating new rows. The flip side: that one engine has accumulated 3,900+ lines in `BusinessBaseServiceImpl` alone, plus another 600+ in `BusinessGdsconfigformsServiceImpl`. Edge cases, special-case table handling (e.g., the `mftproductionplanslave` hardcode at `BusinessBaseServiceImpl.java:1768`), per-tenant overlay merge logic, and the multi-tenant scope-bypass list all live in this single class. Adding a new feature that the universal dispatch doesn't handle means either expanding this class or writing custom code that bypasses it — both of which erode the "one engine handles everything" property the design promised. ## Business-data table prefixes The wiki treats business modules as illustrations rather than subjects, but the schema names them in a regular pattern. A maintainer can map any business-data table to its domain by the three-letter prefix: | Prefix | Domain | Sample tables (live count) | |---|---|---| | `gds` | Framework metadata (modules, forms, fields, permissions, parameters, charts) | `gdsmodule`, `gdsconfigformmaster`, `gdsconfigformslave`, `gdsjurisdiction`, `gdsroute`, `gdsformconst`, `gdsparameter`, `gdsconfigcharmaster`/`slave` (chart definitions used by the BACK 图表配置 screen), … | | `sys` | Framework system (numbering, jurisdiction grants, reports, search, billing settings) — distinct from `gds*` "definition" tier | `sysjurisdiction`, `sysbillnosettings`, `sysreport`, `syssearch`, `sysapi`, `syssystemsettings`, … (68 tables) | | `sis` | Shared lookup tables / classifiers backing dropdowns | `sisbank`, `siscolor`, `sisversionflow`, `sisjurisdictionclassify`, … (80 tables) | | `sft` | Login-session / group-permission link tables | `sftlogininfo`, `sftlogininfojurisdictiongroup`, … (8 tables) | | `ele` | Master data ("element"): customer, employee, machine, materials, product, process, semigoods, costframe | `elecustomer*`, `eleemployee*`, `elemachine*`, `elematerials*`, `eleproduct*`, … (89 tables) | | `mft` | Manufacturing: work-order, production-plan, production-report | `mftworkordermaster`, `mftproductionplan*`, `mftproductionreport*`, … (82 tables) | | `sal` | Sales | `salsalesordermaster`, `salsalesorderslave`, `salsalesorderprocess`, … (67 tables) | | `quo` | Quotation | `quoquotationmaster`, `quoquotationslave`, `quoquotationcalc_tmp`, … (23 tables) | | `acc` | Accounting | `accordercostanalysis`, `accordercostanalysisoperation`, … (31 tables) | | `pur` | Purchasing | `purpurchaseapply`, `purpurchasearrive`, `purpurchasechecking`, … (28 tables) | | `ops` | Outside-processing / outsourcing | `opsoutsidearrive`, `opsoutsidechecking`, `opsoutsideinstore`, … (23 tables) | | `cah` | Cashier / financial | `cahcashierinit`, `cahcostchangemaster`, `cahpaymentmaster`, `cahreceiptmaster`, … (22 tables) | | `sgd` | Semi-goods (半成品) | `sgdsemigoodscheck`, `sgdsemigoodsinstore`, `sgdsemigoodsmatchbill`, … (21 tables) | | `ept` | Equipment / machine fixed assets | `eptmachinefixedborrow`, `eptmachinefixedchange`, `eptmachinefixedinstore`, … (21 tables) | | `mit` | Materials inventory transactions | `mitmaterialsadjust`, `mitmaterialscheck`, `mitmaterialsinstore`, … (19 tables) | | `pit` | Product inventory transactions | `pitproductadjust`, `pitproductbarcode`, `pitproductcheck`, `pitproductinstore`, … (18 tables) | | `qly` | Quality testing | `qlycomematerialstest`, `qlyproducttest`, `qlyprocesstest`, … (8 tables) | | `kpi` | KPI tracking | `kpimaster`, `kpidetail`, `kpimoduleuserday`, … (7 tables) | | `udf` | User-defined / generic-voucher framework | `udfaccountno`, `udfvouchermaster`, `udfvouchertemplatemaster`, … (5 tables) | | `viw_` / `Viw_` | Database **views** (case inconsistent across schema) | `viw_mftworkorderprocess`, `viw_corebusinessreport`, `viw_accordercostanalysisnew`, … (311 views in total) | | `plat_` | B2B printing-platform layer (out of scope per [index](../index.md#whats-out-of-scope)) | 92 tables — not documented here | | `ai_` | AI / LLM features (out of scope) | 7 tables — not documented here | | `act_`, `qrtz_` | Third-party schemas (Activiti workflow, Quartz scheduler) | covered transitively under [Activiti](../reference/maintainer/activiti.md) and [tech-stack Quartz](../reference/maintainer/tech-stack.md#4-workflow-scheduling) | The business-domain prefixes (`ele`, `mft`, `sal`, `quo`, `acc`, `pur`, `ops`, `cah`, `sgd`, `ept`, `mit`, `pit`, `qly`, `kpi`, `udf`) and their slaves all follow the same metadata-driven runtime — there is no per-prefix Java code, just rows in `gdsconfigformmaster` / `gdsconfigformslave` pointing at each backing table or view.