thesis.md 3.51 KB

The data-driven thesis

xly is sold to many printing-industry customers, each of whom wants the ERP to behave a little differently — different forms, different reports, different approval rules, sometimes different stored procedures. The naive solution is a fork per customer: copy the codebase, modify, deploy. That is unmaintainable past two or three customers.

xly's solution is the opposite: a single codebase, a single deployment, and per-customer behaviour expressed as data. The application's modules, forms, fields, dropdowns, permissions, document numbering, even the URL slugs are all rows in metadata tables (gdsmodule, gdsconfigformmaster, gdsconfigformslave, gdsroute, gdsjurisdiction, gdsformconst, …).

The runtime is an interpreter. When a request comes in, the framework loads the relevant rows, joins the user's tenant context onto them, and renders the resulting form / list / report on demand. The Java code is generic; the application's behaviour is in the database. PMs (not engineers) own the metadata and therefore own the application.

The cost

Three costs are baked into this design and worth being explicit about:

  1. Per-request metadata reads. Every page load runs at least four table reads (gdsmodule, gdsconfigformmaster, gdsconfigformslave, gdsjurisdiction) plus tenant filters. The runtime caches aggressively, but those reads are unavoidable on cache miss.

  2. A schema that won't stop growing. New module = a row in gdsmodule plus 1-50 rows in gdsconfigformslave plus a backing physical table (often per-document-type). The base-table count climbs as more business modules are introduced; production tenants typically carry more tables than a clean dev schema, since every customer- bespoke module survives in the shared schema.

  3. Relationships are conventions, not constraints. With FKs disabled for performance and migration agility, every join from gdsconfigformmaster.sParentId to gdsmodule.sId (and a hundred similar joins) is a semantic FK. Orphan rows are possible.

The reward

In exchange xly gets:

  • One codebase serves dozens of customers. Each customer's tenant has its own metadata rows; the Java is identical.
  • PMs evolve the application without engineering time. They open BACK, add a module, define a form, set permissions, and the next user load shows the change.
  • Customizations are layered cleanly (Slice 4): per-tenant overrides sit on top of the shared base without forking.

When it breaks down

Data-driven works until a customer needs behaviour that can't be expressed as metadata — different SQL, different procedure body, an aggregation rule that doesn't fit the framework's vocabulary. xly's escape hatch for that case is the per-customer SQL override channel: hand-written SQL committed to script/客户/<customer>/ and applied directly to that customer's schema, bypassing the framework entirely. That channel is real and used. It is also the most expensive form of customization to maintain.

What this means for reading the wiki

Every slice in this wiki documents one application of the thesis. Slice 1 is the four-table read on a CRUD module. Slice 2 is multi-tenant scoping through every layer. Slice 3 is the read-only / view-backed variant. Slice 4 is the customization overlay. Slice 5 is the escape hatch when the overlay isn't enough. Together they cover the data-driven design from its centre out.