request-lifecycle.md
9.88 KB
The metadata-driven request lifecycle
The diagram you'll come back to. Every metadata-driven page in xly follows this same flow; once you've internalised it, the rest of the framework is variations on a theme.
The flow
┌──────────────────────────────────────────────────────────────────────┐
│ Browser │
│ 1. Loads SPA shell at any URL (the server returns the same shell │
│ for every path; gdsroute is a *client-side* sidebar/deep-link │
│ whitelist, not a server-side 404 gate) │
│ 2. User clicks a sidebar item or navigates within the SPA │
│ 3. SPA decides which module to load → calls /business/... │
└──────────────────────────────────────────────────────────────────────┘
│
│ GET /xlyEntry/business/getModelBysId/{sModelsId}
│ ?sModelsId={sModelsId}
│ (the module's id appears in BOTH path and query —
│ the controller binds the path variable, but the
│ service reads sModelsId from the @RequestParam map,
│ so the SPA must include it in the query string too)
▼
┌──────────────────────────────────────────────────────────────────────┐
│ xlyEntry — BusinessBaseController.getModelBysId() │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ RequestAddParamUtil.addParams(params, userInfo) │ │
│ │ → sBrandsId, sSubsidiaryId, sUserId, sLanguage, … │ │
│ │ (16 keys total — see runtime.md) │ │
│ │ → tenant scope is now AVAILABLE for any downstream query │ │
│ │ that wants it. Framework-metadata reads filter by │ │
│ │ form-id only; per-tenant overlays + business data │ │
│ │ reads filter by sBrandsId/sSubsidiaryId. │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ BusinessBaseService.getModelBysId(map) │
│ │ │
│ ├── 1. formData │
│ │ └── gdsconfigformmaster (filtered by │
│ │ sParentId = sModelsId; gdsmodule itself │
│ │ is *not* SELECT-ed, only referenced by id) │
│ │ + LEFT JOIN gdsconfigformpersonalize │
│ │ (per-tenant overlay) │
│ │ + per-master gdsconfigformslave │
│ │ + per-master gdsconfigformcustomslave │
│ │ (per-tenant overlay) │
│ ├── 2. gdsformconst (filtered by sParentId only; │
│ │ NOT tenant-scoped; sLanguage selects which │
│ │ label column to return) │
│ ├── 3. sysjurisdiction (per-user/group grants joined │
│ │ to sftlogininfojurisdictiongroup + │
│ │ sisjurisdictionclassify; skipped for ADMIN. │
│ │ Returned under map key `gdsjurisdiction` — │
│ │ misleading name, the gdsjurisdiction table is │
│ │ the builder-side action catalogue, not what's │
│ │ read here) │
│ ├── 4. sysbillnosettings (per-tenant, per-form) │
│ └── 5. sysreport (per-tenant, per-form) │
└──────────────────────────────────────────────────────────────────────┘
│
│ Returns one composite map:
│ { formData, gdsformconst, gdsjurisdiction,
│ billnosetting, report }
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Browser SPA │
│ Renders the form using formData; populates dropdowns from │
│ gdsformconst and per-control SQL fetched separately │
│ │
│ Calls /business/getBusinessDataByFormcustomId/{formId} │
│ with sModelsId={moduleId} to load the actual rows │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ xlyEntry — BusinessBaseController.getBusinessDataByFormcustomId() │
│ │
│ Composes parameterised SQL from formMaster.sSqlStr / sWhere / │
│ sOrder, with sBrandsId / sSubsidiaryId injected; │
│ runs against the form's backing object (table | view | proc). │
└──────────────────────────────────────────────────────────────────────┘
│
▼
User sees the grid
The five-key composite
getModelBysId returns one Java Map with these keys, in this order:
| Key | Source | Used by the SPA for |
|---|---|---|
formData |
gdsconfigformmaster (filtered by sParentId = sModelsId) ⋈ gdsconfigformpersonalize (per-tenant overlay); per master row, gdsconfigformslave + gdsconfigformcustomslave overlays. gdsmodule is referenced only by id. |
The form layout itself — every field, control, label, validation rule |
gdsformconst |
gdsformconst rows filtered by sParentId only — NOT tenant-scoped; sLanguage selects which label column to return |
Form-level constants — labels, defaults, dropdown text |
gdsjurisdiction |
sysjurisdiction rows for the user (or for the user's group via sftlogininfojurisdictiongroup ⋈ sisjurisdictionclassify); skipped for ADMIN. The map-key name gdsjurisdiction is misleading — that table is the builder-side action catalogue, not what's read here. |
Per-button and per-data permissions |
billnosetting |
sysbillnosettings row for this module (per-tenant) |
Document-numbering rules (work-order numbers, quotation numbers) |
report |
sysreport rows linked to this form (per-tenant) |
Print templates (Excel via jxls, PDF via iText) |
What's not in this lifecycle
A few things readers expect to find here but don't:
-
The "save" path. Save is its own endpoint
(
POST /business/addUpdateDelBusinessData), bundling add+update+delete in one request. See Slice 1. -
The "open existing row for edit" fetch. Same endpoint as the grid
load (
getBusinessDataByFormcustomId) but with the row'ssIdin the body — the handler branches on whether the body asks for one row or many. -
Workflow steps. When a module has an active approval workflow
(
bCheck = 1, populatedsVersionFlowId, deployed Activiti process), additional steps interleave. None of those tables are populated in this dev DB; see Slice 7 (deferred). -
Cache invalidation. When BACK changes a metadata row, a JMS message
invalidates cached copies on every running node —
ConsumerChangeGdsModuleThreadinxlyErpJmsConsumer. Outside the request flow but adjacent to it.
Variations covered by other slices
- Slice 1 — the canonical instance, with observed network traffic.
- Slice 2 — how the multi-tenant filter threads through every step.
- Slice 3 — view-backed instead of table-backed.
-
Slice 4 — the
gdsconfigformcustomslavemerge step. - Slice 5 — when a stored procedure body has been replaced for a specific tenant.
Read this page once and keep it open
If a future chapter uses a term you don't recognise, it almost certainly refers to one of the boxes in the diagram above. Come back here.