customization-channels.md 4.46 KB

Two customization channels

xly customers customize the system through two distinct paths. Understanding the difference is essential — they have different powers, different costs, and different operational consequences.

Channel 1 — Metadata edits via BACK

The supported, PM-driven path. A customer's implementer (or the customer themselves) opens the BACK builder, navigates to a module, and inserts / updates / deletes rows in metadata tables:

  • gdsmodule to register a new module
  • gdsconfigformmaster + gdsconfigformslave to define a form
  • gdsconfigformcustomslave to add a per-tenant field overlay (Slice 4)
  • gdsconfigformpersonalize to override a form's SQL/where/order per tenant
  • gdsjurisdiction to grant or restrict permissions
  • gdsroute, gdsformconst, sysbillnosettings, … as needed

These edits are data. They travel with the customer's database. They are visible in the BACK UI so a PM can audit them. The framework's runtime reads them on every request (with caching). The Java code is unchanged; the application's behaviour is what those rows say it is.

This is the path the architecture intends customers to use. Whether the actual ratio is 90/10 in favour of Channel 1 isn't measured anywhere; the empirical signal is that 18 customer directories under script/客户/ exist, which is a non-trivial slice of the customer base needing what Channel 1 can't express. Take "90%+ should live here" as an aspirational target, not a measured fact.

Channel 2 — Per-customer SQL overrides

The escape hatch. When metadata cannot express what a customer needs — typically because the customer needs different procedural logic, not just different fields or labels — engineers commit a hand-written SQL file to script/客户/<customer>/<file>.sql in the codebase. The file typically contains a DROP PROCEDURE … CREATE PROCEDURE pair (or a DROP VIEW … CREATE VIEW for view replacements).

The file is then manually applied to the target customer's MySQL schema by a DBA or engineer. From that moment, that customer's database has a literally-different stored procedure with the same name as the standard.

Documented in detail in Slice 5.

This channel is real and used in production — eighteen customers have override directories in the codebase today. It is also significantly more expensive to maintain than Channel 1.

How to choose

Use Channel 1 when:

  • The customization is structural (new field, new module, different label).
  • The behaviour can be expressed by changing SQL fragments (sSqlStr / sWhere / sOrder).
  • The customization should be visible in BACK and editable by a PM.
  • You want it to travel with the database backup.

Use Channel 2 when:

  • The customer needs procedural logic the framework's Add/Update/Calc procs don't express.
  • You need to replace a stored procedure body, not just inject SQL fragments around it.
  • The runtime divergence should live in source-controlled .sql files (under script/客户/<customer>/) so a maintainer reviewing the customer's runtime can see the per-customer changes at a glance, rather than discovering them only by connecting to the live DB.

Channel 2 is almost always a last resort. Reach for it only after confirming Channel 1 cannot do the job.

Cost comparison

Property Channel 1 (metadata) Channel 2 (SQL override)
Travels with DB backup yes only if the customer's schema is the backup target
Visible in BACK yes no
Editable by a PM yes no — engineer-only
Cross-tenant impact none — rows are scoped by sBrandsId none — applied per-customer schema
Re-applies after schema rebuild yes (rows in DB) no — must be manually re-run
Auditable from a single source yes (DB rows + their sMakePerson) partial — file is in repo but the apply step has no log
Discovery by reading codebase requires connecting to DB yes — file lives in script/客户/

What this means for slice readers

When a slice describes "the standard behaviour", it means the behaviour seen by a tenant whose customizations are entirely in Channel 1. A customer with Channel 2 overrides may experience different runtime behaviour for the procs that have been replaced — the framework's controller code can't tell the difference. Reasoning about runtime behaviour for such customers requires looking at the deployed schema, not just the codebase.