request-lifecycle.md 7.9 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 allowed by gdsroute (URL whitelist) │
│    2. User clicks a sidebar item or navigates within the SPA         │
│    3. SPA decides which module to load → calls /business/...         │
└──────────────────────────────────────────────────────────────────────┘
              │
              │  GET /xlyEntry/business/getModelBysId/{moduleId}
              │      ?sModelsId={moduleId}
              ▼
┌──────────────────────────────────────────────────────────────────────┐
│  xlyEntry — BusinessBaseController.getModelBysId()                   │
│                                                                      │
│   ┌──────────────────────────────────────────────────────────────┐   │
│   │  RequestAddParamUtil.addParams(params, userInfo)             │   │
│   │     → sBrandsId, sSubsidiaryId, sUserId, sLanguage, …        │   │
│   │     → tenant scope is now baked into every downstream query  │   │
│   └──────────────────────────────────────────────────────────────┘   │
│                                                                      │
│           BusinessBaseService.getModelBysId(map)                     │
│              │                                                       │
│              ├── load gdsmodule row (the module)                     │
│              ├── load gdsconfigformmaster row(s)                     │
│              │       (joined to module via sParentId)                │
│              ├── load gdsconfigformslave rows                        │
│              │       (joined to form-master via sParentId)           │
│              ├── merge gdsconfigformpersonalize  (per tenant)        │
│              ├── merge gdsconfigformcustomslave  (per tenant)        │
│              ├── load gdsjurisdiction (skipped for ADMIN)            │
│              ├── load gdsformconst (form-level constants)            │
│              ├── load sysbillnosettings (document-numbering)         │
│              └── load sysreport rows linked to this 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 gdsmodulegdsconfigformmastergdsconfigformslave (+ overlays) The form layout itself — every field, control, label, validation rule
gdsformconst gdsformconst rows scoped by tenant + language Form-level constants — labels, defaults, dropdown text
gdsjurisdiction gdsjurisdiction rows for the user's role Per-button and per-data permissions
billnosetting sysbillnosettings row for this module Document-numbering rules (work-order numbers, quotation numbers)
report sysreport rows linked to this form 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's sId in 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, populated sVersionFlowId, 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 — ConsumerChangeGdsModuleThread in xlyErpJmsConsumer. 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 gdsconfigformcustomslave merge 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.