Hydrology XData Flow
Request flow through Delphi 12.x + TMS/XData + UniDAC + Aurelius stack.
High-Level Architecture
- Sparkle/Indy HTTP Server (
TIndySparkleHTTPServer) accepts HTTP requests. - Server Modules (Sphinx + XData modules) are mounted at base paths (e.g.,
/hydrology). - XData Services expose routes like
/HydrologyService/GetStationDataBySelection. - TRootService resolves the tenant from the URL and wires a per-tenant DataManager.
- Business Objects (e.g.,
TStations) call registered stored procedures via UniDAC. - DTOs carry results back to the client.
1. XData Service Contract
The interface defines the model and service name that form the route prefix:
[ServiceContract]
[ServiceName('HydrologyService')]
[Model('HydrologyModel')]
IHydrologyService = INTERFACE(IInvokable)
FUNCTION ImAlive: STRING;
FUNCTION GetStationDataBySelection(DashboardNumber, CardNumber,
Period, PeriodUnit: integer): TList<TStationDTO>;
END;
Routing:
/<base>/HydrologyService/<MethodName>2. Service Registration
// in Services.body.pas
RegisterServiceType(THydrologyService);
3. TRootService: Tenant Resolution
TRootService.DoSetup is invoked per request:
- Derives tenant from URL
- Ensures tenant's
TTenantDatabaseModuleexists - Connects DataManager to tenant's
TUniConnection - Registers stored procedures
4. Business Layer
The request lands with a fully prepared IDataManager:
FUNCTION TStations.GetStationDataBySelection(...): TList<TStationDTO>;
BEGIN
DataManager.LoadProc('GetStationDataBySelectionProc');
// Execute and map to DTOs
END;
5. Stored Procedure Registry
PROCEDURE TQueriesModule.RegisterStationProcedures;
BEGIN
DataManager.ProcedureList.Add('GetStationDataBySelectionProc',
GetStationDataBySelectionProc);
END;
Naming matters: The key must match exactly what the business object uses in
LoadProc().6. Full Request Lifecycle
- Client POSTs to
/hydrology/HydrologyService/GetStationDataBySelection - Sparkle receives on
TIndySparkleHTTPServer - Routing matches hydrology base to XData module
- XData resolves service and method
- TRootService.DoSetup wires tenant DataManager
- Business object uses
LoadProc(), executes - DTOs returned as JSON
7. "Unknown Path" Failures
| Layer | What must be true | What breaks |
|---|---|---|
| HTTP → Module | XData module mounted at /hydrology | Plugin reload leaves module detached |
| XData → Service | Service registered | Different module instance |
| Verb | POST requests | GET retry fails |
| Base path | Client uses correct path | Path changes after reload |
8. Plugin Mount Code
In Plugin.XDataServer.pas:
FUNCTION TXDataServerModule.SetupXDataServer: Boolean;
BEGIN
XDataServer.BaseURL := '/empire/' + PluginInstance.NameSpace;
XDataServer.ModelName := PluginInstance.ModelName;
HttpServerModule := XDataServer.CreateModule;
IF Assigned(HttpServerModule) THEN
Appserver.AddModule(HttpServerModule);
END;
If AddModule is skipped or temporarily undone during reload, requests will fail with "Unknown path".