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. - Carries the URL fragment (
sName), display names (sChinese,sEnglish,sBig5), tree position (sParentId,sAllId), product edition (sVersionFlowId, see Slice 2), 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.
Form
The screen layout for a module — header + N field definitions.
-
Master: one row per form in
gdsconfigformmaster. 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. 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.sFormIdexists 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. - One row per virtual column in
gdsconfigtbslave.
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
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 — 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,500+ lines in
BusinessBaseServiceImpl alone, plus another 800+ 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, … (66 tables) |
sis |
Shared lookup tables / classifiers backing dropdowns |
sisbank, siscolor, sisversionflow, sisjurisdictionclassify, … (78 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*, … (88 tables) |
mft |
Manufacturing: work-order, production-plan, production-report |
mftworkordermaster, mftproductionplan*, mftproductionreport*, … (72 tables) |
sal |
Sales |
salsalesordermaster, salsalesorderslave, salsalesorderprocess, … (65 tables) |
quo |
Quotation |
quoquotationmaster, quoquotationslave, quoquotationcalc_tmp, … (12 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, cahcostchange, cahpayment, cahreceipt, … (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) | 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 and tech-stack Quartz |
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.